Chapter 16

The aim of these exercises is to continue making sure you’re comfortable with handling multivariate data. In this chapter’s, you’ll focus on analyses based on dissimilarities/distances, including fitting linear models to these kinds of response variables.

For each of the examples below, you should follow the sequence we’ve used previously, as far as it’s sensible:

  1. What is the biological question?

  2. Is the predictor continuous or categorical?

  3. Write out the linear model corresponding to this question.

  4. What distribution do you expect the response variable to follow?

  5. What are the assumptions behind the statistical model you’ll fit?

    1. Are those assumptions satisfied?
  6. Fit the model

    1. How will you assess whether the model fits well?

    2. Can you detect an effect of the predictors?

    3. How do you measure the effect?

  7. What do you conclude (including any cautions)


A

Dixon et al. (2018) focused on assemblages of reptiles in forests and woodlands of southeastern Australia, with a particular interest in relationships between these assemblages and the fire history of the landscape. They identified 81 sites that varied in time since fire, from 6 months to >96 y. Rather than a continuum, three categories of time since fire were used, 0.5-2y, 6-12y, and >96y. Sites were also classified according to habitat.

Reptile assemblages were sampled with a range of methods, including visual surveys and camera traps, to give counts of 20 reptiles, whose abundance ranged from 0-1 to 0-126, depending on species.

Data are available from dryad, as Rep_abund.csv. You’ll want to focus on the columns with reptile numbers, and the one with the fire history (tsf). For convenience, we’ve extracted those data as dixonbiota.csv

Start by having a quick look at the data.

df <- read_csv("../data/dixonbiota.csv")
Rows: 79 Columns: 22── Column specification ────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr  (2): site, tsf
dbl (20): adup, aplat, amur, amac, aram, dcor, ecun, esax, eul, htal, ldel, lgui, lwhi, ppor...
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
head(df, 10)

The file is pretty straightforward, with tsf being the fire category, and columns 3-22 each representing one reptile taxon.

Suppose you think of a reptile assemblage as simply the species that are present, regardless of how common they are. What would you conclude about the infuence of time since fire now?

Repeat the preceding analysis after creating a new data file that is presence-absence only.

df1.jac <- vegdist(df1,binary=TRUE,method='jaccard')
#Run 3d first
df1s.mds3 <- metaMDS(df1.jac,k=3,autotransform=FALSE,try=40,trymax=80,maxit=200)
Run 0 stress 0.1404128 
Run 1 stress 0.1478658 
Run 2 stress 0.1416978 
Run 3 stress 0.1405588 
... Procrustes: rmse 0.01500187  max resid 0.08886188 
Run 4 stress 0.1404109 
... New best solution
... Procrustes: rmse 0.002860922  max resid 0.02265594 
Run 5 stress 0.145486 
Run 6 stress 0.1476233 
Run 7 stress 0.1418068 
Run 8 stress 0.1423797 
Run 9 stress 0.1457509 
Run 10 stress 0.1416979 
Run 11 stress 0.1404126 
... Procrustes: rmse 0.002853227  max resid 0.02281661 
Run 12 stress 0.140558 
... Procrustes: rmse 0.01456468  max resid 0.08805689 
Run 13 stress 0.1414712 
Run 14 stress 0.1411415 
Run 15 stress 0.1405617 
... Procrustes: rmse 0.0149327  max resid 0.0899859 
Run 16 stress 0.1415481 
Run 17 stress 0.1404251 
... Procrustes: rmse 0.00228483  max resid 0.01842417 
Run 18 stress 0.1428212 
Run 19 stress 0.1405586 
... Procrustes: rmse 0.01462785  max resid 0.08844371 
Run 20 stress 0.1404136 
... Procrustes: rmse 0.003032611  max resid 0.02427334 
Run 21 stress 0.1405605 
... Procrustes: rmse 0.01485076  max resid 0.08965517 
Run 22 stress 0.1416429 
Run 23 stress 0.1499082 
Run 24 stress 0.1420545 
Run 25 stress 0.1419425 
Run 26 stress 0.1405616 
... Procrustes: rmse 0.01492593  max resid 0.08996847 
Run 27 stress 0.1405633 
... Procrustes: rmse 0.0149849  max resid 0.09014188 
Run 28 stress 0.1404112 
... Procrustes: rmse 0.0001823383  max resid 0.0008661697 
... Similar to previous best
Run 29 stress 0.1418758 
Run 30 stress 0.1485354 
Run 31 stress 0.1419033 
Run 32 stress 0.1458155 
Run 33 stress 0.1404104 
... New best solution
... Procrustes: rmse 0.0002170324  max resid 0.001584222 
... Similar to previous best
Run 34 stress 0.1404102 
... New best solution
... Procrustes: rmse 0.0002560659  max resid 0.001758468 
... Similar to previous best
Run 35 stress 0.1404098 
... New best solution
... Procrustes: rmse 0.001533377  max resid 0.01214581 
Run 36 stress 0.1414897 
Run 37 stress 0.1404105 
... Procrustes: rmse 0.0003876278  max resid 0.003003357 
... Similar to previous best
Run 38 stress 0.1443789 
Run 39 stress 0.1404132 
... Procrustes: rmse 0.002460885  max resid 0.01976426 
Run 40 stress 0.1488112 
*** Best solution repeated 1 times
stressplot(df1s.mds3, main="Shepard plot")

df1s.mds3

Call:
metaMDS(comm = df1.jac, k = 3, try = 40, trymax = 80, autotransform = FALSE,      maxit = 200) 

global Multidimensional Scaling using monoMDS

Data:     df1.jac 
Distance: binary jaccard 

Dimensions: 3 
Stress:     0.1404098 
Stress type 1, weak ties
Best solution was repeated 1 time in 40 tries
The best solution was from try 35 (random start)
Scaling: centring, PC rotation, halfchange scaling 
Species: scores missing
# Now look at 2d
df1s.mds2 <- metaMDS(df1.jac,k=2,autotransform=FALSE,try=40,trymax=80,maxit=200)
Run 0 stress 0.2009509 
Run 1 stress 0.2057315 
Run 2 stress 0.2115578 
Run 3 stress 0.2019149 
Run 4 stress 0.2042377 
Run 5 stress 0.2295765 
Run 6 stress 0.2137388 
Run 7 stress 0.2200574 
Run 8 stress 0.2095372 
Run 9 stress 0.2181162 
Run 10 stress 0.2120083 
Run 11 stress 0.2100096 
Run 12 stress 0.2094365 
Run 13 stress 0.2059035 
Run 14 stress 0.2068707 
Run 15 stress 0.2138507 
Run 16 stress 0.2247804 
Run 17 stress 0.2205491 
Run 18 stress 0.2031129 
Run 19 stress 0.2066478 
Run 20 stress 0.2133806 
Run 21 stress 0.2148553 
Run 22 stress 0.2136219 
Run 23 stress 0.2228445 
Run 24 stress 0.2027397 
Run 25 stress 0.214167 
Run 26 stress 0.2118895 
Run 27 stress 0.2011779 
... Procrustes: rmse 0.005744405  max resid 0.04013641 
Run 28 stress 0.2011764 
... Procrustes: rmse 0.005803269  max resid 0.03983499 
Run 29 stress 0.2031113 
Run 30 stress 0.2172545 
Run 31 stress 0.2109358 
Run 32 stress 0.2081906 
Run 33 stress 0.2095477 
Run 34 stress 0.2156386 
Run 35 stress 0.2184839 
Run 36 stress 0.2011783 
... Procrustes: rmse 0.006120401  max resid 0.03870025 
Run 37 stress 0.2133788 
Run 38 stress 0.2175712 
Run 39 stress 0.2171102 
Run 40 stress 0.2155669 
Run 41 stress 0.209539 
Run 42 stress 0.2014972 
Run 43 stress 0.2135397 
Run 44 stress 0.2148463 
Run 45 stress 0.2145609 
Run 46 stress 0.2042383 
Run 47 stress 0.2122397 
Run 48 stress 0.2221256 
Run 49 stress 0.2075648 
Run 50 stress 0.21128 
Run 51 stress 0.2035834 
Run 52 stress 0.2009593 
... Procrustes: rmse 0.004919144  max resid 0.03284558 
Run 53 stress 0.2262974 
Run 54 stress 0.2096971 
Run 55 stress 0.2233517 
Run 56 stress 0.2040341 
Run 57 stress 0.2081925 
Run 58 stress 0.2031114 
Run 59 stress 0.2030044 
Run 60 stress 0.2035828 
Run 61 stress 0.2045719 
Run 62 stress 0.2135396 
Run 63 stress 0.2209364 
Run 64 stress 0.2156286 
Run 65 stress 0.2015097 
Run 66 stress 0.2168916 
Run 67 stress 0.2036082 
Run 68 stress 0.209978 
Run 69 stress 0.202577 
Run 70 stress 0.2074581 
Run 71 stress 0.2119757 
Run 72 stress 0.2038332 
Run 73 stress 0.2021148 
Run 74 stress 0.2150588 
Run 75 stress 0.21096 
Run 76 stress 0.2260586 
Run 77 stress 0.2030607 
Run 78 stress 0.2131804 
Run 79 stress 0.2133794 
Run 80 stress 0.218075 
*** Best solution was not repeated -- monoMDS stopping criteria:
    13: no. of iterations >= maxit
    67: stress ratio > sratmax
stressplot(df1s.mds2, main="Shepard plot")

df1s.mds2

Call:
metaMDS(comm = df1.jac, k = 2, try = 40, trymax = 80, autotransform = FALSE,      maxit = 200) 

global Multidimensional Scaling using monoMDS

Data:     df1.jac 
Distance: binary jaccard 

Dimensions: 2 
Stress:     0.2009509 
Stress type 1, weak ties
Best solution was not repeated after 80 tries
The best solution was from try 0 (metric scaling or null solution)
Scaling: centring, PC rotation, halfchange scaling 
Species: scores missing

Marginal decision here - stress just over 0.2 for k=2, so might be OK. Stress good for k=3, but 2-d MDS usually better for reporting or showing to audience

a<-as.data.frame(scores(df1s.mds3))
a<-cbind(df[c(1:2)],a)   #Add site names & symbols from original data file
p1<-ggplot(data=a, aes(x=NMDS1, y=NMDS2, color=tsf, ) )+
  geom_point()+
  stat_ellipse()+
  labs(y="MDS2", x="MDS1")+
  scale_shape_manual(values=sym3,
                     name="TSF",
                     guide =
                         guide_legend(label.theme = element_text(size=6), 
                                      title=NULL)
                     )
p2<-ggplot(data=a, aes(x=NMDS1, y=NMDS3, color = tsf, ) )+
  geom_point()+
  stat_ellipse()+
  labs(y="MDS3", x="MDS1")+
  scale_shape_manual(values=sym3,
                     name="TSF",
                     guide =
                         guide_legend(label.theme = element_text(size=6),
                                    title=NULL)
                     )

p1 + p2 + plot_layout(guides='collect') & theme_qk() & scale_color_uchicago()

Unburned sites separate very clearly on MDS1. Pattern clearer than with abundances included

Examine dispersions

df.disp <- betadisper(df1.jac,df$tsf)
anova(df.disp)
Analysis of Variance Table

Response: Distances
          Df  Sum Sq  Mean Sq F value   Pr(>F)   
Groups     2 0.18892 0.094460  6.0078 0.003781 **
Residuals 76 1.19495 0.015723                    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Still some difference, though not as pronounced when only pres-abs used

Do permanova including tsf

df1.ado <- adonis2(df1.jac~tsf,data=df,permutations=999)
print(df1.ado)
Permutation test for adonis under reduced model
Terms added sequentially (first to last)
Permutation: free
Number of permutations: 999

adonis2(formula = df1.jac ~ tsf, data = df, permutations = 999)
         Df SumOfSqs      R2      F Pr(>F)    
tsf       2   3.7853 0.20678 9.9057  0.001 ***
Residual 76  14.5210 0.79322                  
Total    78  18.3062 1.00000                  
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Clear fire signal

Extension activity

Dixon and her colleagues also recorded a range of habitat variables, which we’ve collected for you in dixonenv.csv

dixonenv <- read_csv("../data/dixonenv.csv")
Rows: 79 Columns: 14── Column specification ────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr  (4): site, tsf, veg, aspect
dbl (10): lcwd, shrcov, grcov, litcov, litdep, rocks, elev, warm, cold, twi
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
head(dixonenv, 10)

They recorded Coarse Woody Debris, which was long-transformed, litter cover (litcov), % cover of groundcover (grcov), and % cover of shrubs (shrcov), and rock cover. In the original paper, Dixon et al. were comfortable that these predictors weren’t correlated.

mvabund

dfmv <- mvabund(df1)
df.mv <- manyglm(dfmv~dixonenv$tsf+dixonenv$lcwd+dixonenv$litcov+dixonenv$grcov+dixonenv$shrcov+dixonenv$lrocks,family="poisson")
plot(df.mv)

# still hetero vars so use -ve binomial
df.mv1 <- manyglm(dfmv~dixonenv$tsf+dixonenv$lcwd+dixonenv$litcov+dixonenv$grcov+dixonenv$shrcov+dixonenv$lrocks,family="negative_binomial")
plot(df.mv1)

anova(df.mv1)
Time elapsed: 0 hr 1 min 37 sec
Analysis of Deviance Table

Model: dfmv ~ dixonenv$tsf + dixonenv$lcwd + dixonenv$litcov + dixonenv$grcov + dixonenv$shrcov + dixonenv$lrocks

Multivariate test:
                Res.Df Df.diff   Dev Pr(>Dev)    
(Intercept)         78                           
dixonenv$tsf        76       2 331.6    0.001 ***
dixonenv$lcwd       75       1  61.5    0.001 ***
dixonenv$litcov     74       1  25.9    0.287    
dixonenv$grcov      73       1  36.4    0.073 .  
dixonenv$shrcov     72       1  21.5    0.601    
dixonenv$lrocks     71       1  58.0    0.003 ** 
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Arguments:
 Test statistics calculated assuming uncorrelated response (for faster computation) 
 P-value calculated using 999 iterations via PIT-trap resampling.

B Simple MDS & Permanova

Let’s return to the Hutto and Barrett (2021) example from the Chapter 15 exercises. There, we used similarity-based analyses to explore the relationship between frog assemblages in ponds and the degree of urbanization surrounding. This seems a question that could just as easily be examined using distances or dissimilarities.

Return to the data and assess whether frog assemblages (using the standardized abundance scale or presence-absence) differ with urbanization.

Did your conclusions differ from those obtained using RDA?

df <- read_csv("../data/huttoamph.csv")
Rows: 50 Columns: 14── Column specification ────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr  (1): type
dbl (13): Site, ACRE, BAME, BFOW, GCAR, HCIN, HVER, LCAT, LCLA, LPAL, LSPH, PCRU, PFER
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
head(df,10)
#ponds 9 and 40 had no frogs. Remove for distance analysis
df <- df %>% 
  filter(Site!= 9 & Site !=40)
df

The explanation of the variables is in the previous chapter’s exercises.

df1 <- df[,-(1:2)]   #Stripped back file without labels, leaving only 
df1.bc <- vegdist(df1,'bray')
#Run 2d first
df1.mds2 <- metaMDS(df1.bc,k=2,autotransform=FALSE,try=40,trymax=80,maxit=200)
Run 0 stress 0.196708 
Run 1 stress 0.1874607 
... New best solution
... Procrustes: rmse 0.06607221  max resid 0.2294443 
Run 2 stress 0.1949763 
Run 3 stress 0.1964022 
Run 4 stress 0.1990471 
Run 5 stress 0.1916105 
Run 6 stress 0.2037482 
Run 7 stress 0.1961229 
Run 8 stress 0.1888276 
Run 9 stress 0.1977393 
Run 10 stress 0.2035879 
Run 11 stress 0.2060001 
Run 12 stress 0.1862237 
... New best solution
... Procrustes: rmse 0.1042587  max resid 0.4865567 
Run 13 stress 0.1888252 
Run 14 stress 0.1920872 
Run 15 stress 0.2075707 
Run 16 stress 0.1854506 
... New best solution
... Procrustes: rmse 0.06781964  max resid 0.2195375 
Run 17 stress 0.1974324 
Run 18 stress 0.1853453 
... New best solution
... Procrustes: rmse 0.006310912  max resid 0.03189332 
Run 19 stress 0.203107 
Run 20 stress 0.1888252 
Run 21 stress 0.1875614 
Run 22 stress 0.1862238 
Run 23 stress 0.1973006 
Run 24 stress 0.1853452 
... New best solution
... Procrustes: rmse 0.0001046814  max resid 0.0004231746 
... Similar to previous best
Run 25 stress 0.2077143 
Run 26 stress 0.1967075 
Run 27 stress 0.1862781 
Run 28 stress 0.1933023 
Run 29 stress 0.1877629 
Run 30 stress 0.1862237 
Run 31 stress 0.2165344 
Run 32 stress 0.1862239 
Run 33 stress 0.1929084 
Run 34 stress 0.196071 
Run 35 stress 0.1967906 
Run 36 stress 0.1990753 
Run 37 stress 0.1862236 
Run 38 stress 0.1888204 
Run 39 stress 0.1877626 
Run 40 stress 0.1920872 
*** Best solution repeated 1 times
stressplot(df1.mds2, main="Shepard plot")

df1.mds2

Call:
metaMDS(comm = df1.bc, k = 2, try = 40, trymax = 80, autotransform = FALSE,      maxit = 200) 

global Multidimensional Scaling using monoMDS

Data:     df1.bc 
Distance: bray 

Dimensions: 2 
Stress:     0.1853452 
Stress type 1, weak ties
Best solution was repeated 1 time in 40 tries
The best solution was from try 24 (random start)
Scaling: centring, PC rotation, halfchange scaling 
Species: scores missing
# Now look at 3d
df.mds3 <- metaMDS(df1.bc,k=3,autotransform=FALSE,try=40,trymax=80,maxit=200)
Run 0 stress 0.129187 
Run 1 stress 0.1384284 
Run 2 stress 0.1384287 
Run 3 stress 0.1289617 
... New best solution
... Procrustes: rmse 0.01116102  max resid 0.05648397 
Run 4 stress 0.128962 
... Procrustes: rmse 0.001080951  max resid 0.006138037 
... Similar to previous best
Run 5 stress 0.1380872 
Run 6 stress 0.1291861 
... Procrustes: rmse 0.01054685  max resid 0.05449119 
Run 7 stress 0.1291862 
... Procrustes: rmse 0.01108717  max resid 0.05660768 
Run 8 stress 0.1289618 
... Procrustes: rmse 0.0009977382  max resid 0.005680432 
... Similar to previous best
Run 9 stress 0.1380886 
Run 10 stress 0.1291601 
... Procrustes: rmse 0.009846495  max resid 0.05162542 
Run 11 stress 0.1289617 
... Procrustes: rmse 0.0001180296  max resid 0.000397725 
... Similar to previous best
Run 12 stress 0.1388068 
Run 13 stress 0.1289614 
... New best solution
... Procrustes: rmse 0.0002094611  max resid 0.001134175 
... Similar to previous best
Run 14 stress 0.1291858 
... Procrustes: rmse 0.01061726  max resid 0.05490047 
Run 15 stress 0.138595 
Run 16 stress 0.1384304 
Run 17 stress 0.1291863 
... Procrustes: rmse 0.01062772  max resid 0.0549278 
Run 18 stress 0.1291602 
... Procrustes: rmse 0.009566248  max resid 0.05100092 
Run 19 stress 0.1291864 
... Procrustes: rmse 0.01111886  max resid 0.05672889 
Run 20 stress 0.1383909 
Run 21 stress 0.1380887 
Run 22 stress 0.1384274 
Run 23 stress 0.1384276 
Run 24 stress 0.128962 
... Procrustes: rmse 0.0002702949  max resid 0.00160678 
... Similar to previous best
Run 25 stress 0.1291856 
... Procrustes: rmse 0.01060586  max resid 0.05479499 
Run 26 stress 0.1289617 
... Procrustes: rmse 0.0002416892  max resid 0.001126536 
... Similar to previous best
Run 27 stress 0.1383939 
Run 28 stress 0.1291857 
... Procrustes: rmse 0.01062561  max resid 0.05487515 
Run 29 stress 0.1380879 
Run 30 stress 0.1291609 
... Procrustes: rmse 0.01000971  max resid 0.05217349 
Run 31 stress 0.1291859 
... Procrustes: rmse 0.01092252  max resid 0.05627383 
Run 32 stress 0.1291858 
... Procrustes: rmse 0.01090207  max resid 0.05607242 
Run 33 stress 0.1291861 
... Procrustes: rmse 0.01051196  max resid 0.05451072 
Run 34 stress 0.1289618 
... Procrustes: rmse 0.0002363175  max resid 0.001405223 
... Similar to previous best
Run 35 stress 0.1291857 
... Procrustes: rmse 0.01088096  max resid 0.05598853 
Run 36 stress 0.1289613 
... New best solution
... Procrustes: rmse 0.0001167837  max resid 0.0004408721 
... Similar to previous best
Run 37 stress 0.1314089 
Run 38 stress 0.1384272 
Run 39 stress 0.1289616 
... Procrustes: rmse 0.0002186302  max resid 0.001234739 
... Similar to previous best
Run 40 stress 0.1291857 
... Procrustes: rmse 0.01067024  max resid 0.0551978 
*** Best solution repeated 2 times
stressplot(df.mds3, main="Shepard plot")

df.mds3

Call:
metaMDS(comm = df1.bc, k = 3, try = 40, trymax = 80, autotransform = FALSE,      maxit = 200) 

global Multidimensional Scaling using monoMDS

Data:     df1.bc 
Distance: bray 

Dimensions: 3 
Stress:     0.1289613 
Stress type 1, weak ties
Best solution was repeated 2 times in 40 tries
The best solution was from try 36 (random start)
Scaling: centring, PC rotation, halfchange scaling 
Species: scores missing

2-d is acceptable

a<-as.data.frame(scores(df1.mds2))
a<-cbind(df[c(1:2)],a)   #Add site names & symbols from original data file
ggplot(data=a, aes(x=NMDS1, y=NMDS2, color=type, ) )+
  geom_point()+
  labs(y="MDS2", x="MDS1")+
  scale_shape_manual(values=sym3,
                     name="Urbanization",
                     guide =
                         guide_legend(label.theme = element_text(size=6), 
                                      title=NULL)
                     )+
  theme_qk() + scale_color_uchicago()

df1.ado <- adonis2(df1.bc~type,data=df,permutations=999)
print(df1.ado)
Permutation test for adonis under reduced model
Terms added sequentially (first to last)
Permutation: free
Number of permutations: 999

adonis2(formula = df1.bc ~ type, data = df, permutations = 999)
         Df SumOfSqs      R2      F Pr(>F)  
type      2    0.912 0.07281 1.7669  0.043 *
Residual 45   11.614 0.92719                
Total    47   12.526 1.00000                
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Permanova detects a difference; MDS plot suggests low urbanization may be different from the other two, though worth looking at dispersion as well, as spread of this type is less than the other two on plot

Repeat analysis with presence-absence

df1.jac <- vegdist(df1,binary=TRUE,method=‘jaccard’)

#Run 2d first
df1.jac <- vegdist(df1,binary=TRUE,method='jaccard')
df1.mds2 <- metaMDS(df1.jac,k=2,autotransform=FALSE,try=40,trymax=80,maxit=200)
Run 0 stress 0.1551612 
Run 1 stress 0.1837013 
Run 2 stress 0.1696919 
Run 3 stress 0.1880348 
Run 4 stress 0.1635149 
Run 5 stress 0.1630923 
Run 6 stress 0.1585799 
Run 7 stress 0.1536788 
... New best solution
... Procrustes: rmse 0.01900199  max resid 0.06508032 
Run 8 stress 0.1934746 
Run 9 stress 0.1538245 
... Procrustes: rmse 0.01100139  max resid 0.05222997 
Run 10 stress 0.1609337 
Run 11 stress 0.1622269 
Run 12 stress 0.1704331 
Run 13 stress 0.1602261 
Run 14 stress 0.1538244 
... Procrustes: rmse 0.01099654  max resid 0.05221544 
Run 15 stress 0.1696919 
Run 16 stress 0.1621113 
Run 17 stress 0.1630125 
Run 18 stress 0.1585799 
Run 19 stress 0.166129 
Run 20 stress 0.1641746 
Run 21 stress 0.1586729 
Run 22 stress 0.1887895 
Run 23 stress 0.1585798 
Run 24 stress 0.1606139 
Run 25 stress 0.163515 
Run 26 stress 0.1739632 
Run 27 stress 0.1709241 
Run 28 stress 0.1772585 
Run 29 stress 0.1850913 
Run 30 stress 0.1630924 
Run 31 stress 0.1721432 
Run 32 stress 0.1608313 
Run 33 stress 0.1808947 
Run 34 stress 0.1602261 
Run 35 stress 0.1866374 
Run 36 stress 0.1555137 
Run 37 stress 0.1691747 
Run 38 stress 0.1952929 
Run 39 stress 0.1538807 
... Procrustes: rmse 0.008809693  max resid 0.04417994 
Run 40 stress 0.1806084 
Run 41 stress 0.1991962 
Run 42 stress 0.2043037 
Run 43 stress 0.1646708 
Run 44 stress 0.1608262 
Run 45 stress 0.1634767 
Run 46 stress 0.1586728 
Run 47 stress 0.1585799 
Run 48 stress 0.1627099 
Run 49 stress 0.1565718 
Run 50 stress 0.1748722 
Run 51 stress 0.1627089 
Run 52 stress 0.1622264 
Run 53 stress 0.1758648 
Run 54 stress 0.1586729 
Run 55 stress 0.1724591 
Run 56 stress 0.1777698 
Run 57 stress 0.1606135 
Run 58 stress 0.1835377 
Run 59 stress 0.1591044 
Run 60 stress 0.1538245 
... Procrustes: rmse 0.01100665  max resid 0.0522056 
Run 61 stress 0.1591019 
Run 62 stress 0.1732141 
Run 63 stress 0.1641745 
Run 64 stress 0.1704183 
Run 65 stress 0.1535586 
... New best solution
... Procrustes: rmse 0.0107227  max resid 0.05414397 
Run 66 stress 0.1695828 
Run 67 stress 0.1586729 
Run 68 stress 0.1609028 
Run 69 stress 0.1609341 
Run 70 stress 0.1781204 
Run 71 stress 0.1551611 
Run 72 stress 0.1585798 
Run 73 stress 0.1627113 
Run 74 stress 0.1930708 
Run 75 stress 0.1721433 
Run 76 stress 0.1673092 
Run 77 stress 0.1538804 
... Procrustes: rmse 0.01706038  max resid 0.08568356 
Run 78 stress 0.1585799 
Run 79 stress 0.1535586 
... Procrustes: rmse 0.0002906816  max resid 0.001349569 
... Similar to previous best
*** Best solution repeated 1 times
stressplot(df1.mds2, main="Shepard plot")

df1.mds2

Call:
metaMDS(comm = df1.jac, k = 2, try = 40, trymax = 80, autotransform = FALSE,      maxit = 200) 

global Multidimensional Scaling using monoMDS

Data:     df1.jac 
Distance: binary jaccard 

Dimensions: 2 
Stress:     0.1535586 
Stress type 1, weak ties
Best solution was repeated 1 time in 79 tries
The best solution was from try 65 (random start)
Scaling: centring, PC rotation, halfchange scaling 
Species: scores missing
# Now look at 3d
df.mds3 <- metaMDS(df1.jac,k=3,autotransform=FALSE,try=40,trymax=80,maxit=200)
Run 0 stress 0.09466813 
Run 1 stress 0.09466822 
... Procrustes: rmse 0.0005907259  max resid 0.002917344 
... Similar to previous best
Run 2 stress 0.09466795 
... New best solution
... Procrustes: rmse 0.000422125  max resid 0.001876703 
... Similar to previous best
Run 3 stress 0.1097137 
Run 4 stress 0.1090623 
Run 5 stress 0.09466803 
... Procrustes: rmse 7.232359e-05  max resid 0.0003468283 
... Similar to previous best
Run 6 stress 0.1005051 
Run 7 stress 0.09466808 
... Procrustes: rmse 0.0001823469  max resid 0.000917663 
... Similar to previous best
Run 8 stress 0.09693885 
Run 9 stress 0.09466795 
... Procrustes: rmse 0.0001688021  max resid 0.0006306265 
... Similar to previous best
Run 10 stress 0.09466813 
... Procrustes: rmse 0.0002394501  max resid 0.001007719 
... Similar to previous best
Run 11 stress 0.09466792 
... New best solution
... Procrustes: rmse 0.000156739  max resid 0.0005673598 
... Similar to previous best
Run 12 stress 0.100505 
Run 13 stress 0.09466824 
... Procrustes: rmse 0.0001553529  max resid 0.0006098347 
... Similar to previous best
Run 14 stress 0.09466817 
... Procrustes: rmse 0.0001279451  max resid 0.0004343751 
... Similar to previous best
Run 15 stress 0.1005054 
Run 16 stress 0.09466821 
... Procrustes: rmse 0.0001518064  max resid 0.0006174398 
... Similar to previous best
Run 17 stress 0.09466807 
... Procrustes: rmse 0.0001705385  max resid 0.00053697 
... Similar to previous best
Run 18 stress 0.10049 
Run 19 stress 0.1004898 
Run 20 stress 0.09466785 
... New best solution
... Procrustes: rmse 0.0003178359  max resid 0.001519645 
... Similar to previous best
Run 21 stress 0.09693835 
Run 22 stress 0.09466791 
... Procrustes: rmse 0.0003074555  max resid 0.001502487 
... Similar to previous best
Run 23 stress 0.1090588 
Run 24 stress 0.1005046 
Run 25 stress 0.09466806 
... Procrustes: rmse 0.0001456666  max resid 0.0008120412 
... Similar to previous best
Run 26 stress 0.09466794 
... Procrustes: rmse 0.000286181  max resid 0.001024811 
... Similar to previous best
Run 27 stress 0.1005051 
Run 28 stress 0.09466829 
... Procrustes: rmse 0.0004617523  max resid 0.002151037 
... Similar to previous best
Run 29 stress 0.09466836 
... Procrustes: rmse 0.0002740026  max resid 0.001533354 
... Similar to previous best
Run 30 stress 0.09466781 
... New best solution
... Procrustes: rmse 0.0001601026  max resid 0.0007993518 
... Similar to previous best
Run 31 stress 0.09693822 
Run 32 stress 0.09693828 
Run 33 stress 0.09466811 
... Procrustes: rmse 0.0003134413  max resid 0.00135763 
... Similar to previous best
Run 34 stress 0.09466819 
... Procrustes: rmse 0.0003169054  max resid 0.001196281 
... Similar to previous best
Run 35 stress 0.1090587 
Run 36 stress 0.1115488 
Run 37 stress 0.1005051 
Run 38 stress 0.10049 
Run 39 stress 0.09466797 
... Procrustes: rmse 0.000226344  max resid 0.0005714952 
... Similar to previous best
Run 40 stress 0.09466804 
... Procrustes: rmse 0.0002594792  max resid 0.00106972 
... Similar to previous best
*** Best solution repeated 5 times
stressplot(df.mds3, main="Shepard plot")

df.mds3

Call:
metaMDS(comm = df1.jac, k = 3, try = 40, trymax = 80, autotransform = FALSE,      maxit = 200) 

global Multidimensional Scaling using monoMDS

Data:     df1.jac 
Distance: binary jaccard 

Dimensions: 3 
Stress:     0.09466781 
Stress type 1, weak ties
Best solution was repeated 5 times in 40 tries
The best solution was from try 30 (random start)
Scaling: centring, PC rotation, halfchange scaling 
Species: scores missing

Again, stress OK with 2D, good with 3D. Look at 2D first

a<-as.data.frame(scores(df1.mds2))
a<-cbind(df[c(1:2)],a)   #Add site names & symbols from original data file
ggplot(data=a, aes(x=NMDS1, y=NMDS2, color=type, ) )+
  geom_point()+
  labs(y="MDS2", x="MDS1")+
  scale_shape_manual(values=sym3,
                     name="Urbanization",
                     guide =
                         guide_legend(label.theme = element_text(size=6), 
                                      title=NULL)
                     )+
  theme_qk() + scale_color_uchicago()

df1.ado <- adonis2(df1.jac~type,data=df,permutations=999)
print(df1.ado)
Permutation test for adonis under reduced model
Terms added sequentially (first to last)
Permutation: free
Number of permutations: 999

adonis2(formula = df1.jac ~ type, data = df, permutations = 999)
         Df SumOfSqs      R2      F Pr(>F)
type      2   0.4525 0.04389 1.0328  0.416
Residual 45   9.8587 0.95611              
Total    47  10.3112 1.00000              

No separation seen on Presence-Absence

C

Griffen et al. (2019) examined the oral microbiome of humans, with a focus on the effects of HIV and AntiRetroviral therapy (ART) on this microbiome. They described the microbiome of 341 patients, who fell into one of two categories: HIV-, and HIV+ with ART. They also matched their samples as far as possible for sex, along with other conditions that can influence oral microbiomes (Candida infection, current smoking). We’ll use their records for these other categories as well. There were more HIV+ than - subjects, but within these groups, approximately equal numbers of two sexes, two Candida infection status, and two smoking categories.

The bacteriome was recorded separately using 16S RNA sequencing, which overed just over 600 taxa.

The data are available from their paper, as two Excel files in the supplementary information. The metadata gives HIV status, and a raft of demographic information. We’ll just work with HIV, sex, Candida, and current smoking. A second sheet has the bacteriome. The code chunk below reads this file (from a Downloads folder - you’ll need to modify the file location). It also screens the file for any bacterial taxa that were not present in this particular study, which reduces the bacterial taxa to 599.

library(readxl)
#Read in metadata. Lots of information we don't need, so a couple of iterations to get down to a few columns we want - HIV status, candida, current smoking, gender
df <- read_excel("../data/41598_2019_55703_MOESM3_ESM.xlsx", 
     range = "A1:S342", na = "NA")
df<-select(df, HIV, candida, gender, smoking_current)
head(df)
#df1 is the bacterial data
df1 <- read_excel("../data/41598_2019_55703_MOESM2_ESM.xlsx")
New names:
head(df1,10)
df1 <- df1 %>%
  select(where( ~ is.numeric(.x) && sum(.x) != 0))  #Drops any bacterial taxon not recorded in this study (i.e. where it's all zeroes)
df1 <- df1[,-1] #remove col1, which is sample ID

Use a dissimilarity based approach to assess the combined effects of HIV-ART, sex, Candida infection, and current smoking on the bacteriome

Do these factors act independently on the bacteriome?

Which effects are largest (use some graphical methods)?

First steps: Think about standardization and which distance measure you’ll use. Bray-Curtis seems common for these kind of data, and and counts of different taxa vary widely.

df1s <- wisconsin(df1)   #Common to do some standardisation, and counts of different taxa vary widely
df1s.bc <- vegdist(df1s,'bray')   #With standardisation (wisconsin)

Run factorial model using permanova, based on B-C

df1.ado <- adonis2(df1s.bc~HIV*candida*gender*smoking_current,data=df,permutations=999)
print(df1.ado)
Permutation test for adonis under reduced model
Terms added sequentially (first to last)
Permutation: free
Number of permutations: 999

adonis2(formula = df1s.bc ~ HIV * candida * gender * smoking_current, data = df, permutations = 999)
                                    Df SumOfSqs      R2      F Pr(>F)    
HIV                                  1    1.369 0.01250 4.3776  0.001 ***
candida                              1    1.461 0.01333 4.6694  0.001 ***
gender                               1    0.492 0.00449 1.5737  0.013 *  
smoking_current                      1    1.111 0.01014 3.5513  0.001 ***
HIV:candida                          1    0.318 0.00290 1.0151  0.386    
HIV:gender                           1    0.354 0.00324 1.1332  0.209    
candida:gender                       1    0.257 0.00234 0.8202  0.843    
HIV:smoking_current                  1    0.269 0.00245 0.8584  0.769    
candida:smoking_current              1    0.344 0.00314 1.0989  0.238    
gender:smoking_current               1    0.394 0.00360 1.2602  0.104    
HIV:candida:gender                   1    0.349 0.00319 1.1164  0.234    
HIV:candida:smoking_current          1    0.280 0.00255 0.8943  0.687    
HIV:gender:smoking_current           1    0.265 0.00242 0.8463  0.793    
candida:gender:smoking_current       1    0.254 0.00232 0.8110  0.857    
HIV:candida:gender:smoking_current   1    0.362 0.00330 1.1562  0.190    
Residual                           325  101.664 0.92809                  
Total                              340  109.541 1.00000                  
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Model shows main effects, but no interactions important (or at least, detected)

Could you fit a simpler model to the data?

Run simpler model

df2.ado <- adonis2(df1s.bc~HIV+candida+gender+smoking_current,data=df,permutations=999)
print(df2.ado)
Permutation test for adonis under reduced model
Terms added sequentially (first to last)
Permutation: free
Number of permutations: 999

adonis2(formula = df1s.bc ~ HIV + candida + gender + smoking_current, data = df, permutations = 999)
                 Df SumOfSqs      R2      F Pr(>F)    
HIV               1    1.369 0.01250 4.3775  0.001 ***
candida           1    1.461 0.01333 4.6693  0.001 ***
gender            1    0.492 0.00449 1.5736  0.026 *  
smoking_current   1    1.111 0.01014 3.5512  0.001 ***
Residual        336  105.108 0.95953                  
Total           340  109.541 1.00000                  
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

No change; not too surprising, as all four main effects were detected easily.

Now go to data visualization

#Run 3d first
df1s.mds3 <- metaMDS(df1s.bc,k=3,autotransform=FALSE,try=40,trymax=80,maxit=200)
Run 0 stress 0.1573956 
Run 1 stress 0.1630191 
Run 2 stress 0.1576127 
... Procrustes: rmse 0.0118972  max resid 0.1519299 
Run 3 stress 0.1581654 
Run 4 stress 0.1576599 
... Procrustes: rmse 0.009852819  max resid 0.1527284 
Run 5 stress 0.1574955 
... Procrustes: rmse 0.012444  max resid 0.1517661 
Run 6 stress 0.1587469 
Run 7 stress 0.1575933 
... Procrustes: rmse 0.01163693  max resid 0.1521229 
Run 8 stress 0.1579649 
Run 9 stress 0.1578527 
... Procrustes: rmse 0.006323981  max resid 0.09982249 
Run 10 stress 0.1574957 
... Procrustes: rmse 0.01120014  max resid 0.1523462 
Run 11 stress 0.1577498 
... Procrustes: rmse 0.009888467  max resid 0.1527233 
Run 12 stress 0.157409 
... Procrustes: rmse 0.007115967  max resid 0.09726598 
Run 13 stress 0.1573745 
... New best solution
... Procrustes: rmse 0.01073383  max resid 0.1524203 
Run 14 stress 0.1573637 
... New best solution
... Procrustes: rmse 0.009102594  max resid 0.1079765 
Run 15 stress 0.1578146 
... Procrustes: rmse 0.01085179  max resid 0.153061 
Run 16 stress 0.1579197 
Run 17 stress 0.1579066 
Run 18 stress 0.1575399 
... Procrustes: rmse 0.01237825  max resid 0.1521774 
Run 19 stress 0.1575654 
... Procrustes: rmse 0.00849738  max resid 0.1079133 
Run 20 stress 0.1577392 
... Procrustes: rmse 0.01138932  max resid 0.1522989 
Run 21 stress 0.1577813 
... Procrustes: rmse 0.01136102  max resid 0.1528569 
Run 22 stress 0.159205 
Run 23 stress 0.1579391 
Run 24 stress 0.1591617 
Run 25 stress 0.1577727 
... Procrustes: rmse 0.008387684  max resid 0.0967774 
Run 26 stress 0.1579649 
Run 27 stress 0.1582177 
Run 28 stress 0.1576498 
... Procrustes: rmse 0.01137476  max resid 0.1525317 
Run 29 stress 0.1591301 
Run 30 stress 0.157277 
... New best solution
... Procrustes: rmse 0.008559541  max resid 0.1081297 
Run 31 stress 0.1579428 
Run 32 stress 0.1576009 
... Procrustes: rmse 0.01273404  max resid 0.1520734 
Run 33 stress 0.1576118 
... Procrustes: rmse 0.01247834  max resid 0.1523452 
Run 34 stress 0.1582027 
Run 35 stress 0.158822 
Run 36 stress 0.158023 
Run 37 stress 0.1575531 
... Procrustes: rmse 0.007344253  max resid 0.1083239 
Run 38 stress 0.157274 
... New best solution
... Procrustes: rmse 0.0003543994  max resid 0.003824715 
... Similar to previous best
Run 39 stress 0.1578054 
Run 40 stress 0.1578922 
*** Best solution repeated 1 times
stressplot(df1s.mds3, main="Shepard plot")

df1s.mds3

Call:
metaMDS(comm = df1s.bc, k = 3, try = 40, trymax = 80, autotransform = FALSE,      maxit = 200) 

global Multidimensional Scaling using monoMDS

Data:     df1s.bc 
Distance: bray 

Dimensions: 3 
Stress:     0.157274 
Stress type 1, weak ties
Best solution was repeated 1 time in 40 tries
The best solution was from try 38 (random start)
Scaling: centring, PC rotation, halfchange scaling 
Species: scores missing
# Now look at 2d
df1s.mds2 <- metaMDS(df1s.bc,k=2,autotransform=FALSE,try=40,trymax=80,maxit=200)
Run 0 stress 0.2249552 
Run 1 stress 0.2251211 
... Procrustes: rmse 0.009373779  max resid 0.1481459 
Run 2 stress 0.2255671 
Run 3 stress 0.2261504 
Run 4 stress 0.2254572 
Run 5 stress 0.2257456 
Run 6 stress 0.2265326 
Run 7 stress 0.2270347 
Run 8 stress 0.2258506 
Run 9 stress 0.2258621 
Run 10 stress 0.225423 
... Procrustes: rmse 0.01541529  max resid 0.2310901 
Run 11 stress 0.2248945 
... New best solution
... Procrustes: rmse 0.00452352  max resid 0.08220424 
Run 12 stress 0.2257927 
Run 13 stress 0.2249545 
... Procrustes: rmse 0.004482882  max resid 0.0822275 
Run 14 stress 0.2253883 
... Procrustes: rmse 0.01627316  max resid 0.2347665 
Run 15 stress 0.224955 
... Procrustes: rmse 0.004520226  max resid 0.0822353 
Run 16 stress 0.2250835 
... Procrustes: rmse 0.008578052  max resid 0.1524037 
Run 17 stress 0.2275692 
Run 18 stress 0.2254386 
Run 19 stress 0.2254065 
Run 20 stress 0.2253846 
... Procrustes: rmse 0.01579237  max resid 0.2345835 
Run 21 stress 0.2248481 
... New best solution
... Procrustes: rmse 0.006292327  max resid 0.08260263 
Run 22 stress 0.2264731 
Run 23 stress 0.2250843 
... Procrustes: rmse 0.01060527  max resid 0.1508133 
Run 24 stress 0.2251181 
... Procrustes: rmse 0.008233582  max resid 0.1483655 
Run 25 stress 0.2255503 
Run 26 stress 0.2261628 
Run 27 stress 0.2251126 
... Procrustes: rmse 0.008253967  max resid 0.1490181 
Run 28 stress 0.2265968 
Run 29 stress 0.2248932 
... Procrustes: rmse 0.006309882  max resid 0.08268836 
Run 30 stress 0.2253832 
Run 31 stress 0.2277665 
Run 32 stress 0.2258463 
Run 33 stress 0.2253349 
... Procrustes: rmse 0.009301725  max resid 0.1493075 
Run 34 stress 0.2256928 
Run 35 stress 0.2254409 
Run 36 stress 0.2248496 
... Procrustes: rmse 0.0002922941  max resid 0.005119062 
... Similar to previous best
Run 37 stress 0.2248936 
... Procrustes: rmse 0.006317961  max resid 0.08246939 
Run 38 stress 0.2260217 
Run 39 stress 0.2273473 
Run 40 stress 0.2255819 
*** Best solution repeated 1 times
stressplot(df1s.mds2, main="Shepard plot")

df1s.mds2

Call:
metaMDS(comm = df1s.bc, k = 2, try = 40, trymax = 80, autotransform = FALSE,      maxit = 200) 

global Multidimensional Scaling using monoMDS

Data:     df1s.bc 
Distance: bray 

Dimensions: 2 
Stress:     0.2248481 
Stress type 1, weak ties
Best solution was repeated 1 time in 40 tries
The best solution was from try 21 (random start)
Scaling: centring, PC rotation, halfchange scaling 
Species: scores missing

3D model fits better than 2D. Stress for 2D above 0.2

Start with plots where symbol colour indicates levels of one of the factors

a<-as.data.frame(scores(df1s.mds3))
a<-cbind(df[c(1:4)],a)   #Add site names & symbols from original data file
p1<-ggplot(data=a, aes(x=NMDS1, y=NMDS2, color=HIV) )+
  geom_point()+
  stat_ellipse()+
  labs(y="MDS2", x="MDS1")+
  scale_shape_manual(values=c(16,17),
                     guide =
                         guide_legend(label.theme = element_text(size=6), 
                                      title=NULL)
                     )
p2<-ggplot(data=a, aes(x=NMDS1, y=NMDS3, color = HIV) )+
  geom_point()+
  stat_ellipse()+
  labs(y="MDS3", x="MDS1")+
  scale_shape_manual(values=c(16,17),
                     guide =
                         guide_legend(label.theme = element_text(size=6),
                                    title=NULL)
                     )

p1 + p2 + plot_layout(guides='collect') + plot_annotation(title="HIV status") & theme_qk() & scale_color_uchicago()


p1<-ggplot(data=a, aes(x=NMDS1, y=NMDS2, color=candida) )+
  geom_point()+
  stat_ellipse()+
  labs(y="MDS2", x="MDS1")+
  scale_shape_manual(values=c(16,17),
                     guide =
                         guide_legend(label.theme = element_text(size=6), 
                                      title=NULL)
                     )
p2<-ggplot(data=a, aes(x=NMDS1, y=NMDS3, color = candida) )+
  geom_point()+
  stat_ellipse()+
  labs(y="MDS3", x="MDS1")+
  scale_shape_manual(values=c(16,17),
                     guide =
                         guide_legend(label.theme = element_text(size=6),
                                    title=NULL)
                     )

p1 + p2 + plot_layout(guides='collect') + plot_annotation(title="Candida") & theme_qk() & scale_color_uchicago()


p1<-ggplot(data=a, aes(x=NMDS1, y=NMDS2, color=gender) )+
  geom_point()+
  stat_ellipse()+
  labs(y="MDS2", x="MDS1")+
  scale_shape_manual(values=c(16,17),
                     guide =
                         guide_legend(label.theme = element_text(size=6), 
                                      title=NULL)
                     )
p2<-ggplot(data=a, aes(x=NMDS1, y=NMDS3, color = gender) )+
  geom_point()+
  stat_ellipse()+
  labs(y="MDS3", x="MDS1")+
  scale_shape_manual(values=c(16,17),
                     guide =
                         guide_legend(label.theme = element_text(size=6),
                                    title=NULL)
                     )

p1 + p2 + plot_layout(guides='collect') + plot_annotation(title="Sex") & theme_qk() & scale_color_uchicago()


p1<-ggplot(data=a, aes(x=NMDS1, y=NMDS2, color=smoking_current) )+
  geom_point()+
  stat_ellipse()+
  labs(y="MDS2", x="MDS1")+
  scale_shape_manual(values=c(16,17),
                     guide =
                         guide_legend(label.theme = element_text(size=6), 
                                      title=NULL)
                     )
p2<-ggplot(data=a, aes(x=NMDS1, y=NMDS3, color = smoking_current) )+
  geom_point()+
  stat_ellipse()+
  labs(y="MDS3", x="MDS1")+
  scale_shape_manual(values=c(16,17),
                     guide =
                         guide_legend(label.theme = element_text(size=6),
                                    title=NULL)
                     )

p1 + p2 + plot_layout(guides='collect') + plot_annotation(title="Current smoking") & theme_qk() & scale_color_uchicago()

Try some visualizations of pairs of factors using MDS1 & MDS2 (while 3D is preferred, stress of 3D is around .15, vs 0.22, so not huge difference) Separate HIV+/-, then use symbol colour to separate second factor.

p1<-ggplot(data=subset(a, candida=="Yes"), aes(x=NMDS1, y=NMDS2, color=HIV) )+
  geom_point()+
  stat_ellipse()+
  labs(y="MDS2", x="MDS1",
       title="Candida")+
  scale_shape_manual(
                     guide =
                         guide_legend(label.theme = element_text(size=6), 
                                      title=NULL)
                     )
p2<-ggplot(data=subset(a, candida =="No"), aes(x=NMDS1, y=NMDS2, color = HIV) )+
  geom_point()+
  stat_ellipse()+
  labs(y="MDS2", x="MDS1",
       title = "No Candida")+
  scale_shape_manual(
                     guide =
                         guide_legend(label.theme = element_text(size=6),
                                    title=NULL)
                     )
p_combined <- p1 + p2 + plot_layout(guides='collect') & theme_qk() & scale_color_uchicago()
p_ranges_x <- c(ggplot_build(p_combined[[1]])$layout$panel_scales_x[[1]]$range$range,
  ggplot_build(p_combined[[2]])$layout$panel_scales_x[[1]]$range$range)

p_ranges_y <- c(ggplot_build(p_combined[[1]])$layout$panel_scales_y[[1]]$range$range,
                ggplot_build(p_combined[[2]])$layout$panel_scales_y[[1]]$range$range)
p_combined & 
  xlim(min(p_ranges_x), max(p_ranges_x)) & 
  ylim(min(p_ranges_y), max(p_ranges_y))

p1<-ggplot(data=subset(a, HIV=="Yes"), aes(x=NMDS1, y=NMDS2, color=candida) )+
  geom_point()+
  stat_ellipse()+
  labs(y="MDS2", x="MDS1",
       title="HIV+")+
  scale_shape_manual(
                     guide =
                         guide_legend(label.theme = element_text(size=6), 
                                      title=NULL)
                     )
p2<-ggplot(data=subset(a, HIV =="No"), aes(x=NMDS1, y=NMDS2, color = candida) )+
  geom_point()+
  stat_ellipse()+
  labs(y="MDS2", x="MDS1",
       title = "HIV-")+
  scale_shape_manual(
                     guide =
                         guide_legend(label.theme = element_text(size=6),
                                    title=NULL)
                     )
p_combined <- p1 + p2 + plot_layout(guides='collect') & theme_qk() & scale_color_uchicago()
p_ranges_x <- c(ggplot_build(p_combined[[1]])$layout$panel_scales_x[[1]]$range$range,
  ggplot_build(p_combined[[2]])$layout$panel_scales_x[[1]]$range$range)

p_ranges_y <- c(ggplot_build(p_combined[[1]])$layout$panel_scales_y[[1]]$range$range,
                ggplot_build(p_combined[[2]])$layout$panel_scales_y[[1]]$range$range)
p_combined & 
  xlim(min(p_ranges_x), max(p_ranges_x)) & 
  ylim(min(p_ranges_y), max(p_ranges_y))

p1<-ggplot(data=subset(a, HIV=="Yes"), aes(x=NMDS1, y=NMDS2, color = smoking_current) )+
  geom_point()+
  stat_ellipse()+
  labs(y="MDS2", x="MDS1",
       title="HIV+")+
  scale_shape_manual(
                     guide =
                         guide_legend(label.theme = element_text(size=6), 
                                      title=NULL)
                     )
p2<-ggplot(data=subset(a, HIV =="No"), aes(x=NMDS1, y=NMDS2, color = smoking_current) )+
  geom_point()+
  stat_ellipse()+
  labs(y="MDS2", x="MDS1",
       title = "HIV-")+
  scale_shape_manual(
                     guide =
                         guide_legend(label.theme = element_text(size=6),
                                    title=NULL)
                     )
p_combined <- p1 + p2 + plot_layout(guides='collect') & theme_qk() & scale_color_uchicago()
p_ranges_x <- c(ggplot_build(p_combined[[1]])$layout$panel_scales_x[[1]]$range$range,
  ggplot_build(p_combined[[2]])$layout$panel_scales_x[[1]]$range$range)

p_ranges_y <- c(ggplot_build(p_combined[[1]])$layout$panel_scales_y[[1]]$range$range,
                ggplot_build(p_combined[[2]])$layout$panel_scales_y[[1]]$range$range)
p_combined & 
  xlim(min(p_ranges_x), max(p_ranges_x)) & 
  ylim(min(p_ranges_y), max(p_ranges_y))

References

Dixon, Kelly M., Geoffrey J. Cary, Graeme L. Worboys, and Philip Gibbons. 2018. “The Disproportionate Importance of Long‐unburned Forests and Woodlands for Reptiles.” Ecology and Evolution 8 (22): 10952–63. https://doi.org/gfs587.
Griffen, Ann L., Zachary A. Thompson, Clifford J. Beall, Elizabeth A. Lilly, Carolina Granada, Kelly D. Treas, Kenneth R. DuBois, et al. 2019. “Significant Effect of HIV/HAART on Oral Microbiota Using Multivariate Analysis.” Scientific Reports 9 (1): 19946. https://doi.org/gsfpz3.
Hutto, David, and Kyle Barrett. 2021. “Do Urban Open Spaces Provide Refugia for Frogs in Urban Environments?” Plos One 16 (1). https://doi.org/gr2r9n.
LS0tCm91dHB1dDogCiAgaHRtbF9ub3RlYm9vazoKICAgIHRoZW1lOiBmbGF0bHkKYmlibGlvZ3JhcGh5OiAuLi93ZWJfZXguYmliCi0tLQoKYGBge3IgZWNobz1GQUxTRSwgcmVzdWx0cz0naGlkZSd9CiMgUGFja2FnZXM6IHZlZ2FuLG12YWJ1bmQKc291cmNlKCIuLi9SL2xpYnJhcmllcy5SIikKc291cmNlKCIuLi9SL2FwcGVhcmFuY2UuUiIpCmxpYnJhcnkodmVnYW4pCmxpYnJhcnkobXZhYnVuZCkKYGBgCiMgQ2hhcHRlciAxNgoKVGhlIGFpbSBvZiB0aGVzZSBleGVyY2lzZXMgaXMgdG8gY29udGludWUgbWFraW5nIHN1cmUgeW91J3JlIGNvbWZvcnRhYmxlIHdpdGggaGFuZGxpbmcgbXVsdGl2YXJpYXRlIGRhdGEuIEluIHRoaXMgY2hhcHRlcidzLCB5b3UnbGwgZm9jdXMgb24gYW5hbHlzZXMgYmFzZWQgb24gZGlzc2ltaWxhcml0aWVzL2Rpc3RhbmNlcywgaW5jbHVkaW5nIGZpdHRpbmcgbGluZWFyIG1vZGVscyB0byB0aGVzZSBraW5kcyBvZiByZXNwb25zZSB2YXJpYWJsZXMuCgpGb3IgZWFjaCBvZiB0aGUgZXhhbXBsZXMgYmVsb3csIHlvdSBzaG91bGQgZm9sbG93IHRoZSBzZXF1ZW5jZSB3ZSd2ZSB1c2VkIHByZXZpb3VzbHksIGFzIGZhciBhcyBpdCdzIHNlbnNpYmxlOgoKMS4gIFdoYXQgaXMgdGhlIGJpb2xvZ2ljYWwgcXVlc3Rpb24/CgoyLiAgSXMgdGhlIHByZWRpY3RvciBjb250aW51b3VzIG9yIGNhdGVnb3JpY2FsPwoKMy4gIFdyaXRlIG91dCB0aGUgbGluZWFyIG1vZGVsIGNvcnJlc3BvbmRpbmcgdG8gdGhpcyBxdWVzdGlvbi4KCjQuICBXaGF0IGRpc3RyaWJ1dGlvbiBkbyB5b3UgZXhwZWN0IHRoZSByZXNwb25zZSB2YXJpYWJsZSB0byBmb2xsb3c/Cgo1LiAgV2hhdCBhcmUgdGhlIGFzc3VtcHRpb25zIGJlaGluZCB0aGUgc3RhdGlzdGljYWwgbW9kZWwgeW91J2xsIGZpdD8KCiAgICAxLiAgQXJlIHRob3NlIGFzc3VtcHRpb25zIHNhdGlzZmllZD8KCjYuICBGaXQgdGhlIG1vZGVsCgogICAgMS4gIEhvdyB3aWxsIHlvdSBhc3Nlc3Mgd2hldGhlciB0aGUgbW9kZWwgZml0cyB3ZWxsPwoKICAgIDIuICBDYW4geW91IGRldGVjdCBhbiBlZmZlY3Qgb2YgdGhlIHByZWRpY3RvcnM/CgogICAgMy4gIEhvdyBkbyB5b3UgbWVhc3VyZSB0aGUgZWZmZWN0PwoKNy4gIFdoYXQgZG8geW91IGNvbmNsdWRlIChpbmNsdWRpbmcgYW55IGNhdXRpb25zKQoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIyBBCgpAZGl4b25EaXNwcm9wb3J0aW9uYXRlSW1wb3J0YW5jZUxvbmcyMDE4IGZvY3VzZWQgb24gYXNzZW1ibGFnZXMgb2YgcmVwdGlsZXMgaW4gZm9yZXN0cyBhbmQgd29vZGxhbmRzIG9mIHNvdXRoZWFzdGVybiBBdXN0cmFsaWEsIHdpdGggYSBwYXJ0aWN1bGFyIGludGVyZXN0IGluIHJlbGF0aW9uc2hpcHMgYmV0d2VlbiB0aGVzZSBhc3NlbWJsYWdlcyBhbmQgdGhlIGZpcmUgaGlzdG9yeSBvZiB0aGUgbGFuZHNjYXBlLiBUaGV5IGlkZW50aWZpZWQgODEgc2l0ZXMgdGhhdCB2YXJpZWQgaW4gdGltZSBzaW5jZSBmaXJlLCBmcm9tIDYgbW9udGhzIHRvIFw+OTYgeS4gUmF0aGVyIHRoYW4gYSBjb250aW51dW0sIHRocmVlIGNhdGVnb3JpZXMgb2YgdGltZSBzaW5jZSBmaXJlIHdlcmUgdXNlZCwgMC41LTJ5LCA2LTEyeSwgYW5kIFw+OTZ5LiBTaXRlcyB3ZXJlIGFsc28gY2xhc3NpZmllZCBhY2NvcmRpbmcgdG8gaGFiaXRhdC4KClJlcHRpbGUgYXNzZW1ibGFnZXMgd2VyZSBzYW1wbGVkIHdpdGggYSByYW5nZSBvZiBtZXRob2RzLCBpbmNsdWRpbmcgdmlzdWFsIHN1cnZleXMgYW5kIGNhbWVyYSB0cmFwcywgdG8gZ2l2ZSBjb3VudHMgb2YgMjAgcmVwdGlsZXMsIHdob3NlIGFidW5kYW5jZSByYW5nZWQgZnJvbSAwLTEgdG8gMC0xMjYsIGRlcGVuZGluZyBvbiBzcGVjaWVzLgoKRGF0YSBhcmUgYXZhaWxhYmxlIGZyb20gW2RyeWFkXShodHRwczovL2RhdGFkcnlhZC5vcmcvc3Rhc2gvZGF0YXNldC9kb2k6MTAuNTA2MS9kcnlhZC5naDVtNDcxKSwgYXMgUmVwX2FidW5kLmNzdi4gWW91J2xsIHdhbnQgdG8gZm9jdXMgb24gdGhlIGNvbHVtbnMgd2l0aCByZXB0aWxlIG51bWJlcnMsIGFuZCB0aGUgb25lIHdpdGggdGhlIGZpcmUgaGlzdG9yeSAodHNmKS4gRm9yIGNvbnZlbmllbmNlLCB3ZSd2ZSBleHRyYWN0ZWQgdGhvc2UgZGF0YSBhcyBkaXhvbmJpb3RhLmNzdgoKKipTdGFydCBieSBoYXZpbmcgYSBxdWljayBsb29rIGF0IHRoZSBkYXRhLioqCgpgYGB7cn0KZGYgPC0gcmVhZF9jc3YoIi4uL2RhdGEvZGl4b25iaW90YS5jc3YiKQpoZWFkKGRmLCAxMCkKYGBgCgpUaGUgZmlsZSBpcyBwcmV0dHkgc3RyYWlnaHRmb3J3YXJkLCB3aXRoIHRzZiBiZWluZyB0aGUgZmlyZSBjYXRlZ29yeSwgYW5kIGNvbHVtbnMgMy0yMiBlYWNoIHJlcHJlc2VudGluZyBvbmUgcmVwdGlsZSB0YXhvbi4KCiMjIE91dGxpbmUgaG93IHlvdSdkIGFzc2VzcyB3aGV0aGVyIHRoZSByZXB0aWxlIGFzc2VtYmxhZ2UgKHRoZSBjb21iaW5hdGlvbiBvZiBzcGVjaWVzIHByZXNlbnQgYW5kIHRoZWlyIGFidW5kYW5jZXMpIGlzIHJlbGF0ZWQgdG8gZmlyZSBoaXN0b3J5LCB1c2luZyBhIGRpc3NpbWlsYXJpdHktYmFzZWQgYXBwcm9hY2guCgpZb3Ugc2hvdWxkIHRoaW5rIGFib3V0IGhvdyB5b3UnbGwgZGVhbCB3aXRoIHRoZSBkYXRhOgoKLSAgIFdpbGwgeW91IG5lZWQgYSB0cmFuc2Zvcm1hdGlvbiwgc3RhbmRhcmRpemF0aW9uLCBldGMuPwoKLSAgIFdoYXQgYXJlIHRoZSBjb25zZXF1ZW5jZXMgb2YgYW55IGRlY2lzaW9ucyB5b3UgbWFrZSBmb3IgdGhlIGludGVycHJldGF0aW9uIG9mIHlvdXIgYW5hbHlzaXM/CgotICAgV2hhdCBtZWFzdXJlKHMpIG9mIGRpc3NpbWlsYXJpdHkgYXJlIGFwcHJvcHJpYXRlPwoKIyMjIFJ1biB0aGUgYW5hbHlzaXMgYW5kIHByb3ZpZGUgeW91ciBpbnRlcnByZXRhdGlvbgoKPiAjIyMgTURTIG9uIHJlcHRpbGUgYWJ1bmRhbmNlcwoKPiBTdGFydCB3aXRoIGRpc3NpbWlsYXJpdGllcwo+Cj4gU3R1ZGVudHMgc2hvdWxkIGhhdmUgc29tZSBkaXNjdXNzaW9uIG9mIHJhdyB2cyB0cmFuc2Zvcm1lZCAoZS5nLiA0dGggcm9vdCksIHN0YW5kYXJkaXphdGlvbi4KPgo+IEZvdXJ0aCByb290IGlzIGNvbW1vbi4gVXNpbmcgaXQgZG93bndlaWdodHMgdGhlIGluZmx1ZW5jZSBvZiBhYnVuZGFudCBzcGVjaWVzLCB3aGlsZSByYXcgYWxsb3dzIHRob3NlIHNwZWNpZXMgdG8gaGF2ZSBhIHN0cm9uZyBpbmZsdWVuY2UuIFRoZXJlIGFyZSBxdWVzdGlvbnMgYWJvdXQgd2hpY2ggb25lIG9mIHRoZXNlIHJlZmxlY3RzIGVjb2xvZ2ljYWwgZnVuY3Rpb24gYmV0dGVyCj4KPiBEYXRhIGFyZSBhbGwgYWJ1bmRhbmNlcwoKYGBge3J9CmRmMSA8LSBkZlssLSgxOjIpXSAgICNTdHJpcHBlZCBiYWNrIGZpbGUgd2l0aG91dCBsYWJlbHMsIGxlYXZpbmcgb25seSAKZGYxcyA8LSB3aXNjb25zaW4oZGYxKSAgICNDb21tb24gdG8gZG8gc29tZSBzdGFuZGFyZGlzYXRpb24KZGYxcy5iYyA8LSB2ZWdkaXN0KGRmMXMsJ2JyYXknKSAgICNXaXRoIHN0YW5kYXJkaXNhdGlvbiAod2lzY29uc2luKQpgYGAKCj4gVmlzdWFsaXplIHJlc3VsdHMKCj4gTmVlZCB0byBkZWNpZGUgd2hldGhlciB0byB1c2UgMiBvciAzIGRpbWVuc2lvbnMKCmBgYHtyfQojUnVuIDNkIGZpcnN0CmRmMXMubWRzMyA8LSBtZXRhTURTKGRmMXMuYmMsaz0zLGF1dG90cmFuc2Zvcm09RkFMU0UsdHJ5PTQwLHRyeW1heD04MCxtYXhpdD0yMDApCnN0cmVzc3Bsb3QoZGYxcy5tZHMzLCBtYWluPSJTaGVwYXJkIHBsb3QiKQpkZjFzLm1kczMKIyBOb3cgbG9vayBhdCAyZApkZjFzLm1kczIgPC0gbWV0YU1EUyhkZjFzLmJjLGs9MixhdXRvdHJhbnNmb3JtPUZBTFNFLHRyeT00MCx0cnltYXg9ODAsbWF4aXQ9MjAwKQpzdHJlc3NwbG90KGRmMXMubWRzMiwgbWFpbj0iU2hlcGFyZCBwbG90IikKZGYxcy5tZHMyCmBgYAoKPiBTdHJlc3MgdmFsdWVzIHN1Z2dlc3QgM2QgcGxvdCBpcyBhIG1vcmUgYWNjdXJhdGUgcmVwcmVzZW50YXRpb24KCj4gTmVlZCB0byBwbG90IGRpbSAxIHZzIGRpbSAyIGFuZCBkaW0gMSB2cyBkaW0gMyB3aXRoIHRzZiBncm91cHMgaWRlbnRpZmllZCBieSBzeW1ib2xzCj4KPiAqKkdlbmVyYXRlIGdyYXBocyoqCj4KPiBUaGlzIGlzIGEgdmFyaWFudCBvZiBjb2RlIHVzZWQgaW4gd29ya2VkIGV4YW1wbGUgZm9yIENoIDE2LiBJdCBkb2VzIHVzZSB0aGUgZ3JhcGggc2V0dGluZ3MgZGVmaW5lZCBpbiAqYXBwZWFyYW5jZS5SKiwgd2hpY2ggaXMgY2FsbGVkIGVhcmxpZXIuCgpgYGB7cn0KYTwtYXMuZGF0YS5mcmFtZShzY29yZXMoZGYxcy5tZHMzKSkKYTwtY2JpbmQoZGZbYygxOjIpXSxhKSAgICNBZGQgc2l0ZSBuYW1lcyAmIHN5bWJvbHMgZnJvbSBvcmlnaW5hbCBkYXRhIGZpbGUKcDE8LWdncGxvdChkYXRhPWEsIGFlcyh4PU5NRFMxLCB5PU5NRFMyLCBjb2xvcj10c2YsIGdyb3VwID0gdHNmKSApKwogIGdlb21fcG9pbnQoKSsKICBzdGF0X2VsbGlwc2UoKSsKICBsYWJzKHk9Ik1EUzIiLCB4PSJNRFMxIikrCiAgc2NhbGVfc2hhcGVfbWFudWFsKHZhbHVlcz1zeW0zLAogICAgICAgICAgICAgICAgICAgICBuYW1lPSJUU0YiLAogICAgICAgICAgICAgICAgICAgICBndWlkZSA9CiAgICAgICAgICAgICAgICAgICAgICAgICBndWlkZV9sZWdlbmQobGFiZWwudGhlbWUgPSBlbGVtZW50X3RleHQoc2l6ZT02KSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGl0bGU9TlVMTCkKICAgICAgICAgICAgICAgICAgICAgKQpwMjwtZ2dwbG90KGRhdGE9YSwgYWVzKHg9Tk1EUzEsIHk9Tk1EUzMsIGNvbG9yID0gdHNmLCBncm91cCA9IHRzZiApICkrCiAgZ2VvbV9wb2ludCgpKwogIHN0YXRfZWxsaXBzZSgpKwogIGxhYnMoeT0iTURTMyIsIHg9Ik1EUzEiKSsKICBzY2FsZV9zaGFwZV9tYW51YWwodmFsdWVzPXN5bTMsCiAgICAgICAgICAgICAgICAgICAgIG5hbWU9IlRTRiIsCiAgICAgICAgICAgICAgICAgICAgIGd1aWRlID0KICAgICAgICAgICAgICAgICAgICAgICAgIGd1aWRlX2xlZ2VuZChsYWJlbC50aGVtZSA9IGVsZW1lbnRfdGV4dChzaXplPTYpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aXRsZT1OVUxMKQogICAgICAgICAgICAgICAgICAgICApCgpwMSArIHAyICsgcGxvdF9sYXlvdXQoZ3VpZGVzPSdjb2xsZWN0JykgJiB0aGVtZV9xaygpICYgc2NhbGVfY29sb3JfdWNoaWNhZ28oKQpgYGAKCiMjIyBIb3cgd291bGQgeW91IGZpdCBhIGxpbmVhciBtb2RlbCB0byBhc3Nlc3MgZmlyZSBlZmZlY3RzPwoKPlRha2UgYSBsb29rIGF0IG11bHRpdmFyaWF0ZSBkaXNwZXJzaW9uIGZpcnN0CgpgYGB7cn0KZGYuZGlzcCA8LSBiZXRhZGlzcGVyKGRmMXMuYmMsZGYkdHNmKQphbm92YShkZi5kaXNwKQpgYGAKPiBTdWdnZXN0aW9uIG9mIHN1YnN0YW50aWFsIGRpZmZlcmVuY2VzIGluIGRpc3BlcnNpb24uIExvbmcgVFNGIHNpdGVzIHNlZW0gcXVpdGUgc2ltaWxhciwgdGhvc2Ugd2l0aCBzaG9ydGVyIHRpbWVzIGFyZSBtb3JlIHZhcmlhYmxlIGFuZCBzaW1pbGFyIHRvIGVhY2ggb3RoZXIuCgo+IERvIHBlcm1hbm92YSBpbmNsdWRpbmcgdHNmCgpgYGB7cn0KZGYxcy5hZG8gPC0gYWRvbmlzMihkZjFzLmJjfnRzZixkYXRhPWRmLHBlcm11dGF0aW9ucz05OTkpCnByaW50KGRmMXMuYWRvKQpgYGAKCj4gUGVybWFub3ZhIHN1Z2dlc3RzIHN0cm9uZyBzaWduYWwgZnJvbSB0c2YKCj4gU0lNUEVSIGFuYWx5c2lzIGZvciB0c2YgZ3JvdXAgZGlmZmVyZW5jZXMKCmBgYHtyfQpkZi5zaW0gPC0gc2ltcGVyKGRmMXNbLC0oMToyKV0sIGRmJHRzZiwgcGVybXV0YXRpb25zPTEwMDApCnN1bW1hcnkoZGYuc2ltKQpgYGAKCiMjIFN1cHBvc2UgeW91IHRoaW5rIG9mIGEgcmVwdGlsZSBhc3NlbWJsYWdlIGFzIHNpbXBseSB0aGUgc3BlY2llcyB0aGF0IGFyZSBwcmVzZW50LCByZWdhcmRsZXNzIG9mIGhvdyBjb21tb24gdGhleSBhcmUuIFdoYXQgd291bGQgeW91IGNvbmNsdWRlIGFib3V0IHRoZSBpbmZ1ZW5jZSBvZiB0aW1lIHNpbmNlIGZpcmUgbm93PwoKPiBSZXBlYXQgdGhlIHByZWNlZGluZyBhbmFseXNpcyBhZnRlciBjcmVhdGluZyBhIG5ldyBkYXRhIGZpbGUgdGhhdCBpcyBwcmVzZW5jZS1hYnNlbmNlIG9ubHkuCgpgYGB7cn0KZGYxLmphYyA8LSB2ZWdkaXN0KGRmMSxiaW5hcnk9VFJVRSxtZXRob2Q9J2phY2NhcmQnKQojUnVuIDNkIGZpcnN0CmRmMXMubWRzMyA8LSBtZXRhTURTKGRmMS5qYWMsaz0zLGF1dG90cmFuc2Zvcm09RkFMU0UsdHJ5PTQwLHRyeW1heD04MCxtYXhpdD0yMDApCnN0cmVzc3Bsb3QoZGYxcy5tZHMzLCBtYWluPSJTaGVwYXJkIHBsb3QiKQpkZjFzLm1kczMKIyBOb3cgbG9vayBhdCAyZApkZjFzLm1kczIgPC0gbWV0YU1EUyhkZjEuamFjLGs9MixhdXRvdHJhbnNmb3JtPUZBTFNFLHRyeT00MCx0cnltYXg9ODAsbWF4aXQ9MjAwKQpzdHJlc3NwbG90KGRmMXMubWRzMiwgbWFpbj0iU2hlcGFyZCBwbG90IikKZGYxcy5tZHMyCmBgYAoKPiBNYXJnaW5hbCBkZWNpc2lvbiBoZXJlIC0gc3RyZXNzIGp1c3Qgb3ZlciAwLjIgZm9yIGs9Miwgc28gbWlnaHQgYmUgT0suIFN0cmVzcyBnb29kIGZvciBrPTMsIGJ1dCAyLWQgTURTIHVzdWFsbHkgYmV0dGVyIGZvciByZXBvcnRpbmcgb3Igc2hvd2luZyB0byBhdWRpZW5jZQoKYGBge3J9CmE8LWFzLmRhdGEuZnJhbWUoc2NvcmVzKGRmMXMubWRzMykpCmE8LWNiaW5kKGRmW2MoMToyKV0sYSkgICAjQWRkIHNpdGUgbmFtZXMgJiBzeW1ib2xzIGZyb20gb3JpZ2luYWwgZGF0YSBmaWxlCnAxPC1nZ3Bsb3QoZGF0YT1hLCBhZXMoeD1OTURTMSwgeT1OTURTMiwgY29sb3I9dHNmLCApICkrCiAgZ2VvbV9wb2ludCgpKwogIHN0YXRfZWxsaXBzZSgpKwogIGxhYnMoeT0iTURTMiIsIHg9Ik1EUzEiKSsKICBzY2FsZV9zaGFwZV9tYW51YWwodmFsdWVzPXN5bTMsCiAgICAgICAgICAgICAgICAgICAgIG5hbWU9IlRTRiIsCiAgICAgICAgICAgICAgICAgICAgIGd1aWRlID0KICAgICAgICAgICAgICAgICAgICAgICAgIGd1aWRlX2xlZ2VuZChsYWJlbC50aGVtZSA9IGVsZW1lbnRfdGV4dChzaXplPTYpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aXRsZT1OVUxMKQogICAgICAgICAgICAgICAgICAgICApCnAyPC1nZ3Bsb3QoZGF0YT1hLCBhZXMoeD1OTURTMSwgeT1OTURTMywgY29sb3IgPSB0c2YsICkgKSsKICBnZW9tX3BvaW50KCkrCiAgc3RhdF9lbGxpcHNlKCkrCiAgbGFicyh5PSJNRFMzIiwgeD0iTURTMSIpKwogIHNjYWxlX3NoYXBlX21hbnVhbCh2YWx1ZXM9c3ltMywKICAgICAgICAgICAgICAgICAgICAgbmFtZT0iVFNGIiwKICAgICAgICAgICAgICAgICAgICAgZ3VpZGUgPQogICAgICAgICAgICAgICAgICAgICAgICAgZ3VpZGVfbGVnZW5kKGxhYmVsLnRoZW1lID0gZWxlbWVudF90ZXh0KHNpemU9NiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRpdGxlPU5VTEwpCiAgICAgICAgICAgICAgICAgICAgICkKCnAxICsgcDIgKyBwbG90X2xheW91dChndWlkZXM9J2NvbGxlY3QnKSAmIHRoZW1lX3FrKCkgJiBzY2FsZV9jb2xvcl91Y2hpY2FnbygpCmBgYAoKPiBVbmJ1cm5lZCBzaXRlcyBzZXBhcmF0ZSB2ZXJ5IGNsZWFybHkgb24gTURTMS4gUGF0dGVybiBjbGVhcmVyIHRoYW4gd2l0aCBhYnVuZGFuY2VzIGluY2x1ZGVkCgo+RXhhbWluZSBkaXNwZXJzaW9ucwoKYGBge3J9CmRmLmRpc3AgPC0gYmV0YWRpc3BlcihkZjEuamFjLGRmJHRzZikKYW5vdmEoZGYuZGlzcCkKYGBgCj5TdGlsbCBzb21lIGRpZmZlcmVuY2UsIHRob3VnaCBub3QgYXMgcHJvbm91bmNlZCB3aGVuIG9ubHkgcHJlcy1hYnMgdXNlZAoKPiBEbyBwZXJtYW5vdmEgaW5jbHVkaW5nIHRzZgoKYGBge3J9CmRmMS5hZG8gPC0gYWRvbmlzMihkZjEuamFjfnRzZixkYXRhPWRmLHBlcm11dGF0aW9ucz05OTkpCnByaW50KGRmMS5hZG8pCmBgYAoKPiBDbGVhciBmaXJlIHNpZ25hbAoKIyMgRXh0ZW5zaW9uIGFjdGl2aXR5CgpEaXhvbiBhbmQgaGVyIGNvbGxlYWd1ZXMgYWxzbyByZWNvcmRlZCBhIHJhbmdlIG9mIGhhYml0YXQgdmFyaWFibGVzLCB3aGljaCB3ZSd2ZSBjb2xsZWN0ZWQgZm9yIHlvdSBpbiBkaXhvbmVudi5jc3YKCmBgYHtyfQpkaXhvbmVudiA8LSByZWFkX2NzdigiLi4vZGF0YS9kaXhvbmVudi5jc3YiKQpoZWFkKGRpeG9uZW52LCAxMCkKYGBgCgpUaGV5IHJlY29yZGVkIENvYXJzZSBXb29keSBEZWJyaXMsIHdoaWNoIHdhcyBsb25nLXRyYW5zZm9ybWVkLCBsaXR0ZXIgY292ZXIgKGxpdGNvdiksICUgY292ZXIgb2YgZ3JvdW5kY292ZXIgKGdyY292KSwgYW5kICUgY292ZXIgb2Ygc2hydWJzIChzaHJjb3YpLCBhbmQgcm9jayBjb3Zlci4gSW4gdGhlIG9yaWdpbmFsIHBhcGVyLCBEaXhvbiBldCBhbC4gd2VyZSBjb21mb3J0YWJsZSB0aGF0IHRoZXNlIHByZWRpY3RvcnMgd2VyZW4ndCBjb3JyZWxhdGVkLgoKIyMjIEhvdyBpcyByZXB0aWxlIGFzc2VtYmxhZ2UgcmVsYXRlZCB0byB0aW1lIHNpbmNlIGZpcmUgYW5kIHRoZXNlIGZpdmUgaGFiaXRhdCB2YXJpYWJsZXMKCj4gQ2hlY2sgY29udGludW91cyBwcmVkaWN0b3JzCgpgYGB7cn0KYm94cGxvdChkaXhvbmVudiRsY3dkKQpib3hwbG90KGRpeG9uZW52JGxpdGNvdikKYm94cGxvdChkaXhvbmVudiRncmNvdikKYm94cGxvdChkaXhvbmVudiRzaHJjb3YpCmJveHBsb3QoZGl4b25lbnYkcm9ja3MpCiN0cmFuc2Zvcm0gcm9ja3MgdG8gbG9nKzEKZGl4b25lbnYkbHJvY2tzIDwtIGxvZzEwKGRpeG9uZW52JHJvY2tzKzEpCiMgY2hlY2sgaG9tb2cgb2YgZGlzcGVyc2lvbnMgb24gZG91YmxlIHN0YW5kYXJkaXplZCBhYnVuZGFuY2VzCmRmMXMuZGlzcCA8LSBiZXRhZGlzcGVyKGRmMXMuYmMsZGYkdHNmKQphbm92YShkZjFzLmRpc3ApCmBgYAoKPiBkbyBwZXJtYW5vdmEgaW5jbHVkaW5nIHRzZiBwbHVzIGNvbnRpbnVvdXMgcHJlZGljdG9ycwoKYGBge3J9CmRmMXMuYWRvIDwtIGFkb25pczIoZGYxcy5iY350c2YrbGN3ZCtsaXRjb3YrZ3Jjb3Yrc2hyY292K2xyb2NrcyxkYXRhPWRpeG9uZW52LHBlcm11dGF0aW9ucz05OTkpCnByaW50KGRmMXMuYWRvKQojUnVuIGFuYWx5c2lzIGZvciBwcmVzZW5jZS1hYnNlbmNlIGFzIHdlbGwKZGYxLmphYy5hZG8gPC0gYWRvbmlzMihkZjEuamFjfnRzZitsY3dkK2xpdGNvditncmNvditzaHJjb3YrbHJvY2tzLGRhdGE9ZGl4b25lbnYscGVybXV0YXRpb25zPTk5OSkKcHJpbnQoZGYxLmphYy5hZG8pCgpgYGAKCj4gTWFpbiBlZmZlY3QgaXMgdHNmOyBvbmx5IHNvbWUgcXVlc3Rpb24gYWJvdXQgQ1dEIHdoZW4gd2UgbG9vayBhdCBzcGVjaWVzIGNvbXBvc2l0aW9uIGFuZCBhYnVuZGFuY2UsIHdoaWxlIGZvciBwcmVzZW5jZS1hYnNlbmNlIGRhdGEsIHRoZXJlIGFyZSBhbHNvIGVmZmVjdHMgb2YgQ1dEIGFuZCBncm91bmRjb3ZlcgoKIyMjIG12YWJ1bmQKCmBgYHtyIGVycm9yPVRSVUV9CmRmbXYgPC0gbXZhYnVuZChkZjEpCmRmLm12IDwtIG1hbnlnbG0oZGZtdn5kaXhvbmVudiR0c2YrZGl4b25lbnYkbGN3ZCtkaXhvbmVudiRsaXRjb3YrZGl4b25lbnYkZ3Jjb3YrZGl4b25lbnYkc2hyY292K2RpeG9uZW52JGxyb2NrcyxmYW1pbHk9InBvaXNzb24iKQpwbG90KGRmLm12KQojIHN0aWxsIGhldGVybyB2YXJzIHNvIHVzZSAtdmUgYmlub21pYWwKZGYubXYxIDwtIG1hbnlnbG0oZGZtdn5kaXhvbmVudiR0c2YrZGl4b25lbnYkbGN3ZCtkaXhvbmVudiRsaXRjb3YrZGl4b25lbnYkZ3Jjb3YrZGl4b25lbnYkc2hyY292K2RpeG9uZW52JGxyb2NrcyxmYW1pbHk9Im5lZ2F0aXZlX2Jpbm9taWFsIikKcGxvdChkZi5tdjEpCmFub3ZhKGRmLm12MSkKYGBgCgojIyBCIFNpbXBsZSBNRFMgJiBQZXJtYW5vdmEKCkxldCdzIHJldHVybiB0byB0aGUgQGh1dHRvVXJiYW5PcGVuU3BhY2VzMjAyMSBleGFtcGxlIGZyb20gdGhlIENoYXB0ZXIgMTUgZXhlcmNpc2VzLiBUaGVyZSwgd2UgdXNlZCBzaW1pbGFyaXR5LWJhc2VkIGFuYWx5c2VzIHRvIGV4cGxvcmUgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGZyb2cgYXNzZW1ibGFnZXMgaW4gcG9uZHMgYW5kIHRoZSBkZWdyZWUgb2YgdXJiYW5pemF0aW9uIHN1cnJvdW5kaW5nLiBUaGlzIHNlZW1zIGEgcXVlc3Rpb24gdGhhdCBjb3VsZCBqdXN0IGFzIGVhc2lseSBiZSBleGFtaW5lZCB1c2luZyBkaXN0YW5jZXMgb3IgZGlzc2ltaWxhcml0aWVzLgoKUmV0dXJuIHRvIHRoZSBkYXRhIGFuZCBhc3Nlc3Mgd2hldGhlciBmcm9nIGFzc2VtYmxhZ2VzICh1c2luZyB0aGUgc3RhbmRhcmRpemVkIGFidW5kYW5jZSBzY2FsZSBvciBwcmVzZW5jZS1hYnNlbmNlKSBkaWZmZXIgd2l0aCB1cmJhbml6YXRpb24uCgojIyMjIERpZCB5b3VyIGNvbmNsdXNpb25zIGRpZmZlciBmcm9tIHRob3NlIG9idGFpbmVkIHVzaW5nIFJEQT8KCmBgYHtyfQpkZiA8LSByZWFkX2NzdigiLi4vZGF0YS9odXR0b2FtcGguY3N2IikKaGVhZChkZiwxMCkKI3BvbmRzIDkgYW5kIDQwIGhhZCBubyBmcm9ncy4gUmVtb3ZlIGZvciBkaXN0YW5jZSBhbmFseXNpcwpkZiA8LSBkZiAlPiUgCiAgZmlsdGVyKFNpdGUhPSA5ICYgU2l0ZSAhPTQwKQpkZgpgYGAKClRoZSBleHBsYW5hdGlvbiBvZiB0aGUgdmFyaWFibGVzIGlzIGluIHRoZSBwcmV2aW91cyBjaGFwdGVyJ3MgZXhlcmNpc2VzLgoKYGBge3J9CmRmMSA8LSBkZlssLSgxOjIpXSAgICNTdHJpcHBlZCBiYWNrIGZpbGUgd2l0aG91dCBsYWJlbHMsIGxlYXZpbmcgb25seSAKZGYxLmJjIDwtIHZlZ2Rpc3QoZGYxLCdicmF5JykKYGBgCgpgYGB7cn0KI1J1biAyZCBmaXJzdApkZjEubWRzMiA8LSBtZXRhTURTKGRmMS5iYyxrPTIsYXV0b3RyYW5zZm9ybT1GQUxTRSx0cnk9NDAsdHJ5bWF4PTgwLG1heGl0PTIwMCkKc3RyZXNzcGxvdChkZjEubWRzMiwgbWFpbj0iU2hlcGFyZCBwbG90IikKZGYxLm1kczIKIyBOb3cgbG9vayBhdCAzZApkZi5tZHMzIDwtIG1ldGFNRFMoZGYxLmJjLGs9MyxhdXRvdHJhbnNmb3JtPUZBTFNFLHRyeT00MCx0cnltYXg9ODAsbWF4aXQ9MjAwKQpzdHJlc3NwbG90KGRmLm1kczMsIG1haW49IlNoZXBhcmQgcGxvdCIpCmRmLm1kczMKYGBgCgo+IDItZCBpcyBhY2NlcHRhYmxlCgpgYGB7cn0KYTwtYXMuZGF0YS5mcmFtZShzY29yZXMoZGYxLm1kczIpKQphPC1jYmluZChkZltjKDE6MildLGEpICAgI0FkZCBzaXRlIG5hbWVzICYgc3ltYm9scyBmcm9tIG9yaWdpbmFsIGRhdGEgZmlsZQpnZ3Bsb3QoZGF0YT1hLCBhZXMoeD1OTURTMSwgeT1OTURTMiwgY29sb3I9dHlwZSwgKSApKwogIGdlb21fcG9pbnQoKSsKICBsYWJzKHk9Ik1EUzIiLCB4PSJNRFMxIikrCiAgc2NhbGVfc2hhcGVfbWFudWFsKHZhbHVlcz1zeW0zLAogICAgICAgICAgICAgICAgICAgICBuYW1lPSJVcmJhbml6YXRpb24iLAogICAgICAgICAgICAgICAgICAgICBndWlkZSA9CiAgICAgICAgICAgICAgICAgICAgICAgICBndWlkZV9sZWdlbmQobGFiZWwudGhlbWUgPSBlbGVtZW50X3RleHQoc2l6ZT02KSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGl0bGU9TlVMTCkKICAgICAgICAgICAgICAgICAgICAgKSsKICB0aGVtZV9xaygpICsgc2NhbGVfY29sb3JfdWNoaWNhZ28oKQpgYGAKCmBgYHtyfQpkZjEuYWRvIDwtIGFkb25pczIoZGYxLmJjfnR5cGUsZGF0YT1kZixwZXJtdXRhdGlvbnM9OTk5KQpwcmludChkZjEuYWRvKQpgYGAKCj4gUGVybWFub3ZhIGRldGVjdHMgYSBkaWZmZXJlbmNlOyBNRFMgcGxvdCBzdWdnZXN0cyBsb3cgdXJiYW5pemF0aW9uIG1heSBiZSBkaWZmZXJlbnQgZnJvbSB0aGUgb3RoZXIgdHdvLCB0aG91Z2ggd29ydGggbG9va2luZyBhdCBkaXNwZXJzaW9uIGFzIHdlbGwsIGFzIHNwcmVhZCBvZiB0aGlzIHR5cGUgaXMgbGVzcyB0aGFuIHRoZSBvdGhlciB0d28gb24gcGxvdAoKPiBSZXBlYXQgYW5hbHlzaXMgd2l0aCBwcmVzZW5jZS1hYnNlbmNlCgpkZjEuamFjIFw8LSB2ZWdkaXN0KGRmMSxiaW5hcnk9VFJVRSxtZXRob2Q9J2phY2NhcmQnKQoKYGBge3J9CiNSdW4gMmQgZmlyc3QKZGYxLmphYyA8LSB2ZWdkaXN0KGRmMSxiaW5hcnk9VFJVRSxtZXRob2Q9J2phY2NhcmQnKQpkZjEubWRzMiA8LSBtZXRhTURTKGRmMS5qYWMsaz0yLGF1dG90cmFuc2Zvcm09RkFMU0UsdHJ5PTQwLHRyeW1heD04MCxtYXhpdD0yMDApCnN0cmVzc3Bsb3QoZGYxLm1kczIsIG1haW49IlNoZXBhcmQgcGxvdCIpCmRmMS5tZHMyCiMgTm93IGxvb2sgYXQgM2QKZGYubWRzMyA8LSBtZXRhTURTKGRmMS5qYWMsaz0zLGF1dG90cmFuc2Zvcm09RkFMU0UsdHJ5PTQwLHRyeW1heD04MCxtYXhpdD0yMDApCnN0cmVzc3Bsb3QoZGYubWRzMywgbWFpbj0iU2hlcGFyZCBwbG90IikKZGYubWRzMwpgYGAKCj4gQWdhaW4sIHN0cmVzcyBPSyB3aXRoIDJELCBnb29kIHdpdGggM0QuIExvb2sgYXQgMkQgZmlyc3QKCmBgYHtyfQphPC1hcy5kYXRhLmZyYW1lKHNjb3JlcyhkZjEubWRzMikpCmE8LWNiaW5kKGRmW2MoMToyKV0sYSkgICAjQWRkIHNpdGUgbmFtZXMgJiBzeW1ib2xzIGZyb20gb3JpZ2luYWwgZGF0YSBmaWxlCmdncGxvdChkYXRhPWEsIGFlcyh4PU5NRFMxLCB5PU5NRFMyLCBjb2xvcj10eXBlLCApICkrCiAgZ2VvbV9wb2ludCgpKwogIHN0YXRfZWxsaXBzZSgpKwogIGxhYnMoeT0iTURTMiIsIHg9Ik1EUzEiKSsKICBzY2FsZV9zaGFwZV9tYW51YWwodmFsdWVzPXN5bTMsCiAgICAgICAgICAgICAgICAgICAgIG5hbWU9IlVyYmFuaXphdGlvbiIsCiAgICAgICAgICAgICAgICAgICAgIGd1aWRlID0KICAgICAgICAgICAgICAgICAgICAgICAgIGd1aWRlX2xlZ2VuZChsYWJlbC50aGVtZSA9IGVsZW1lbnRfdGV4dChzaXplPTYpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aXRsZT1OVUxMKQogICAgICAgICAgICAgICAgICAgICApKwogIHRoZW1lX3FrKCkgKyBzY2FsZV9jb2xvcl91Y2hpY2FnbygpCmBgYAoKYGBge3J9CmRmMS5hZG8gPC0gYWRvbmlzMihkZjEuamFjfnR5cGUsZGF0YT1kZixwZXJtdXRhdGlvbnM9OTk5KQpwcmludChkZjEuYWRvKQpgYGAKCj4gTm8gc2VwYXJhdGlvbiBzZWVuIG9uIFByZXNlbmNlLUFic2VuY2UKCiMjIEMKCkBncmlmZmVuU2lnbmlmaWNhbnRFZmZlY3RISVYyMDE5IGV4YW1pbmVkIHRoZSBvcmFsIG1pY3JvYmlvbWUgb2YgaHVtYW5zLCB3aXRoIGEgZm9jdXMgb24gdGhlIGVmZmVjdHMgb2YgSElWIGFuZCBBbnRpUmV0cm92aXJhbCB0aGVyYXB5IChBUlQpIG9uIHRoaXMgbWljcm9iaW9tZS4gVGhleSBkZXNjcmliZWQgdGhlIG1pY3JvYmlvbWUgb2YgMzQxIHBhdGllbnRzLCB3aG8gZmVsbCBpbnRvIG9uZSBvZiB0d28gY2F0ZWdvcmllczogSElWXi1eLCBhbmQgSElWKyB3aXRoIEFSVC4gVGhleSBhbHNvIG1hdGNoZWQgdGhlaXIgc2FtcGxlcyBhcyBmYXIgYXMgcG9zc2libGUgZm9yIHNleCwgYWxvbmcgd2l0aCBvdGhlciBjb25kaXRpb25zIHRoYXQgY2FuIGluZmx1ZW5jZSBvcmFsIG1pY3JvYmlvbWVzICgqQ2FuZGlkYSogaW5mZWN0aW9uLCBjdXJyZW50IHNtb2tpbmcpLiBXZSdsbCB1c2UgdGhlaXIgcmVjb3JkcyBmb3IgdGhlc2Ugb3RoZXIgY2F0ZWdvcmllcyBhcyB3ZWxsLiBUaGVyZSB3ZXJlIG1vcmUgSElWKyB0aGFuIC0gc3ViamVjdHMsIGJ1dCB3aXRoaW4gdGhlc2UgZ3JvdXBzLCBhcHByb3hpbWF0ZWx5IGVxdWFsIG51bWJlcnMgb2YgdHdvIHNleGVzLCB0d28gKkNhbmRpZGEqIGluZmVjdGlvbiBzdGF0dXMsIGFuZCB0d28gc21va2luZyBjYXRlZ29yaWVzLgoKVGhlIGJhY3RlcmlvbWUgd2FzIHJlY29yZGVkIHNlcGFyYXRlbHkgdXNpbmcgMTZTIFJOQSBzZXF1ZW5jaW5nLCB3aGljaCBvdmVyZWQganVzdCBvdmVyIDYwMCB0YXhhLgoKVGhlIGRhdGEgYXJlIGF2YWlsYWJsZSBmcm9tIHRoZWlyIHBhcGVyLCBhcyB0d28gRXhjZWwgZmlsZXMgaW4gdGhlIHN1cHBsZW1lbnRhcnkgaW5mb3JtYXRpb24uIFRoZSBtZXRhZGF0YSBnaXZlcyBISVYgc3RhdHVzLCBhbmQgYSByYWZ0IG9mIGRlbW9ncmFwaGljIGluZm9ybWF0aW9uLiBXZSdsbCBqdXN0IHdvcmsgd2l0aCBISVYsIHNleCwgKkNhbmRpZGEqLCBhbmQgY3VycmVudCBzbW9raW5nLiBBIHNlY29uZCBzaGVldCBoYXMgdGhlIGJhY3RlcmlvbWUuIFRoZSBjb2RlIGNodW5rIGJlbG93IHJlYWRzIHRoaXMgZmlsZSAoZnJvbSBhIERvd25sb2FkcyBmb2xkZXIgLSB5b3UnbGwgbmVlZCB0byBtb2RpZnkgdGhlIGZpbGUgbG9jYXRpb24pLiBJdCBhbHNvIHNjcmVlbnMgdGhlIGZpbGUgZm9yIGFueSBiYWN0ZXJpYWwgdGF4YSB0aGF0IHdlcmUgbm90IHByZXNlbnQgaW4gdGhpcyBwYXJ0aWN1bGFyIHN0dWR5LCB3aGljaCByZWR1Y2VzIHRoZSBiYWN0ZXJpYWwgdGF4YSB0byA1OTkuCgpgYGB7ciBkZn0KbGlicmFyeShyZWFkeGwpCiNSZWFkIGluIG1ldGFkYXRhLiBMb3RzIG9mIGluZm9ybWF0aW9uIHdlIGRvbid0IG5lZWQsIHNvIGEgY291cGxlIG9mIGl0ZXJhdGlvbnMgdG8gZ2V0IGRvd24gdG8gYSBmZXcgY29sdW1ucyB3ZSB3YW50IC0gSElWIHN0YXR1cywgY2FuZGlkYSwgY3VycmVudCBzbW9raW5nLCBnZW5kZXIKZGYgPC0gcmVhZF9leGNlbCgiLi4vZGF0YS80MTU5OF8yMDE5XzU1NzAzX01PRVNNM19FU00ueGxzeCIsIAogICAgIHJhbmdlID0gIkExOlMzNDIiLCBuYSA9ICJOQSIpCmRmPC1zZWxlY3QoZGYsIEhJViwgY2FuZGlkYSwgZ2VuZGVyLCBzbW9raW5nX2N1cnJlbnQpCmhlYWQoZGYpCiNkZjEgaXMgdGhlIGJhY3RlcmlhbCBkYXRhCmRmMSA8LSByZWFkX2V4Y2VsKCIuLi9kYXRhLzQxNTk4XzIwMTlfNTU3MDNfTU9FU00yX0VTTS54bHN4IikKaGVhZChkZjEsMTApCmRmMSA8LSBkZjEgJT4lCiAgc2VsZWN0KHdoZXJlKCB+IGlzLm51bWVyaWMoLngpICYmIHN1bSgueCkgIT0gMCkpICAjRHJvcHMgYW55IGJhY3RlcmlhbCB0YXhvbiBub3QgcmVjb3JkZWQgaW4gdGhpcyBzdHVkeSAoaS5lLiB3aGVyZSBpdCdzIGFsbCB6ZXJvZXMpCmRmMSA8LSBkZjFbLC0xXSAjcmVtb3ZlIGNvbDEsIHdoaWNoIGlzIHNhbXBsZSBJRApgYGAKCiMjIyBVc2UgYSBkaXNzaW1pbGFyaXR5IGJhc2VkIGFwcHJvYWNoIHRvIGFzc2VzcyB0aGUgY29tYmluZWQgZWZmZWN0cyBvZiBISVYtQVJULCBzZXgsICpDYW5kaWRhKiBpbmZlY3Rpb24sIGFuZCBjdXJyZW50IHNtb2tpbmcgb24gdGhlIGJhY3RlcmlvbWUKCioqRG8gdGhlc2UgZmFjdG9ycyBhY3QgaW5kZXBlbmRlbnRseSBvbiB0aGUgYmFjdGVyaW9tZT8qKgoKKipXaGljaCBlZmZlY3RzIGFyZSBsYXJnZXN0ICh1c2Ugc29tZSBncmFwaGljYWwgbWV0aG9kcyk/KioKCioqRmlyc3Qgc3RlcHM6KiogVGhpbmsgYWJvdXQgc3RhbmRhcmRpemF0aW9uIGFuZCB3aGljaCBkaXN0YW5jZSBtZWFzdXJlIHlvdSdsbCB1c2UuIEJyYXktQ3VydGlzIHNlZW1zIGNvbW1vbiBmb3IgdGhlc2Uga2luZCBvZiBkYXRhLCBhbmQgYW5kIGNvdW50cyBvZiBkaWZmZXJlbnQgdGF4YSB2YXJ5IHdpZGVseS4KCmBgYHtyIEJDfQpkZjFzIDwtIHdpc2NvbnNpbihkZjEpICAgI0NvbW1vbiB0byBkbyBzb21lIHN0YW5kYXJkaXNhdGlvbiwgYW5kIGNvdW50cyBvZiBkaWZmZXJlbnQgdGF4YSB2YXJ5IHdpZGVseQpkZjFzLmJjIDwtIHZlZ2Rpc3QoZGYxcywnYnJheScpICAgI1dpdGggc3RhbmRhcmRpc2F0aW9uICh3aXNjb25zaW4pCmBgYAoKPlJ1biBmYWN0b3JpYWwgbW9kZWwgdXNpbmcgcGVybWFub3ZhLCBiYXNlZCBvbiBCLUMKCmBgYHtyfQpkZjEuYWRvIDwtIGFkb25pczIoZGYxcy5iY35ISVYqY2FuZGlkYSpnZW5kZXIqc21va2luZ19jdXJyZW50LGRhdGE9ZGYscGVybXV0YXRpb25zPTk5OSkKcHJpbnQoZGYxLmFkbykKYGBgCgo+TW9kZWwgc2hvd3MgbWFpbiBlZmZlY3RzLCBidXQgbm8gaW50ZXJhY3Rpb25zIGltcG9ydGFudCAob3IgYXQgbGVhc3QsIGRldGVjdGVkKQoKKipDb3VsZCB5b3UgZml0IGEgc2ltcGxlciBtb2RlbCB0byB0aGUgZGF0YT8qKgoKPlJ1biBzaW1wbGVyIG1vZGVsCgpgYGB7cn0KZGYyLmFkbyA8LSBhZG9uaXMyKGRmMXMuYmN+SElWK2NhbmRpZGErZ2VuZGVyK3Ntb2tpbmdfY3VycmVudCxkYXRhPWRmLHBlcm11dGF0aW9ucz05OTkpCnByaW50KGRmMi5hZG8pCmBgYAoKPk5vIGNoYW5nZTsgbm90IHRvbyBzdXJwcmlzaW5nLCBhcyBhbGwgZm91ciBtYWluIGVmZmVjdHMgd2VyZSBkZXRlY3RlZCBlYXNpbHkuCgo+Tm93IGdvIHRvIGRhdGEgdmlzdWFsaXphdGlvbgoKYGBge3J9CiNSdW4gM2QgZmlyc3QKZGYxcy5tZHMzIDwtIG1ldGFNRFMoZGYxcy5iYyxrPTMsYXV0b3RyYW5zZm9ybT1GQUxTRSx0cnk9NDAsdHJ5bWF4PTgwLG1heGl0PTIwMCkKc3RyZXNzcGxvdChkZjFzLm1kczMsIG1haW49IlNoZXBhcmQgcGxvdCIpCmRmMXMubWRzMwojIE5vdyBsb29rIGF0IDJkCmRmMXMubWRzMiA8LSBtZXRhTURTKGRmMXMuYmMsaz0yLGF1dG90cmFuc2Zvcm09RkFMU0UsdHJ5PTQwLHRyeW1heD04MCxtYXhpdD0yMDApCnN0cmVzc3Bsb3QoZGYxcy5tZHMyLCBtYWluPSJTaGVwYXJkIHBsb3QiKQpkZjFzLm1kczIKYGBgCgoKPjNEIG1vZGVsIGZpdHMgYmV0dGVyIHRoYW4gMkQuIFN0cmVzcyBmb3IgMkQgYWJvdmUgMC4yCgo+IFN0YXJ0IHdpdGggcGxvdHMgd2hlcmUgc3ltYm9sIGNvbG91ciBpbmRpY2F0ZXMgbGV2ZWxzIG9mIG9uZSBvZiB0aGUgZmFjdG9ycwoKYGBge3J9CmE8LWFzLmRhdGEuZnJhbWUoc2NvcmVzKGRmMXMubWRzMykpCmE8LWNiaW5kKGRmW2MoMTo0KV0sYSkgICAjQWRkIHNpdGUgbmFtZXMgJiBzeW1ib2xzIGZyb20gb3JpZ2luYWwgZGF0YSBmaWxlCnAxPC1nZ3Bsb3QoZGF0YT1hLCBhZXMoeD1OTURTMSwgeT1OTURTMiwgY29sb3I9SElWKSApKwogIGdlb21fcG9pbnQoKSsKICBzdGF0X2VsbGlwc2UoKSsKICBsYWJzKHk9Ik1EUzIiLCB4PSJNRFMxIikrCiAgc2NhbGVfc2hhcGVfbWFudWFsKHZhbHVlcz1jKDE2LDE3KSwKICAgICAgICAgICAgICAgICAgICAgZ3VpZGUgPQogICAgICAgICAgICAgICAgICAgICAgICAgZ3VpZGVfbGVnZW5kKGxhYmVsLnRoZW1lID0gZWxlbWVudF90ZXh0KHNpemU9NiksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRpdGxlPU5VTEwpCiAgICAgICAgICAgICAgICAgICAgICkKcDI8LWdncGxvdChkYXRhPWEsIGFlcyh4PU5NRFMxLCB5PU5NRFMzLCBjb2xvciA9IEhJVikgKSsKICBnZW9tX3BvaW50KCkrCiAgc3RhdF9lbGxpcHNlKCkrCiAgbGFicyh5PSJNRFMzIiwgeD0iTURTMSIpKwogIHNjYWxlX3NoYXBlX21hbnVhbCh2YWx1ZXM9YygxNiwxNyksCiAgICAgICAgICAgICAgICAgICAgIGd1aWRlID0KICAgICAgICAgICAgICAgICAgICAgICAgIGd1aWRlX2xlZ2VuZChsYWJlbC50aGVtZSA9IGVsZW1lbnRfdGV4dChzaXplPTYpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aXRsZT1OVUxMKQogICAgICAgICAgICAgICAgICAgICApCgpwMSArIHAyICsgcGxvdF9sYXlvdXQoZ3VpZGVzPSdjb2xsZWN0JykgKyBwbG90X2Fubm90YXRpb24odGl0bGU9IkhJViBzdGF0dXMiKSAmIHRoZW1lX3FrKCkgJiBzY2FsZV9jb2xvcl91Y2hpY2FnbygpCgpwMTwtZ2dwbG90KGRhdGE9YSwgYWVzKHg9Tk1EUzEsIHk9Tk1EUzIsIGNvbG9yPWNhbmRpZGEpICkrCiAgZ2VvbV9wb2ludCgpKwogIHN0YXRfZWxsaXBzZSgpKwogIGxhYnMoeT0iTURTMiIsIHg9Ik1EUzEiKSsKICBzY2FsZV9zaGFwZV9tYW51YWwodmFsdWVzPWMoMTYsMTcpLAogICAgICAgICAgICAgICAgICAgICBndWlkZSA9CiAgICAgICAgICAgICAgICAgICAgICAgICBndWlkZV9sZWdlbmQobGFiZWwudGhlbWUgPSBlbGVtZW50X3RleHQoc2l6ZT02KSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGl0bGU9TlVMTCkKICAgICAgICAgICAgICAgICAgICAgKQpwMjwtZ2dwbG90KGRhdGE9YSwgYWVzKHg9Tk1EUzEsIHk9Tk1EUzMsIGNvbG9yID0gY2FuZGlkYSkgKSsKICBnZW9tX3BvaW50KCkrCiAgc3RhdF9lbGxpcHNlKCkrCiAgbGFicyh5PSJNRFMzIiwgeD0iTURTMSIpKwogIHNjYWxlX3NoYXBlX21hbnVhbCh2YWx1ZXM9YygxNiwxNyksCiAgICAgICAgICAgICAgICAgICAgIGd1aWRlID0KICAgICAgICAgICAgICAgICAgICAgICAgIGd1aWRlX2xlZ2VuZChsYWJlbC50aGVtZSA9IGVsZW1lbnRfdGV4dChzaXplPTYpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aXRsZT1OVUxMKQogICAgICAgICAgICAgICAgICAgICApCgpwMSArIHAyICsgcGxvdF9sYXlvdXQoZ3VpZGVzPSdjb2xsZWN0JykgKyBwbG90X2Fubm90YXRpb24odGl0bGU9IkNhbmRpZGEiKSAmIHRoZW1lX3FrKCkgJiBzY2FsZV9jb2xvcl91Y2hpY2FnbygpCgpwMTwtZ2dwbG90KGRhdGE9YSwgYWVzKHg9Tk1EUzEsIHk9Tk1EUzIsIGNvbG9yPWdlbmRlcikgKSsKICBnZW9tX3BvaW50KCkrCiAgc3RhdF9lbGxpcHNlKCkrCiAgbGFicyh5PSJNRFMyIiwgeD0iTURTMSIpKwogIHNjYWxlX3NoYXBlX21hbnVhbCh2YWx1ZXM9YygxNiwxNyksCiAgICAgICAgICAgICAgICAgICAgIGd1aWRlID0KICAgICAgICAgICAgICAgICAgICAgICAgIGd1aWRlX2xlZ2VuZChsYWJlbC50aGVtZSA9IGVsZW1lbnRfdGV4dChzaXplPTYpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aXRsZT1OVUxMKQogICAgICAgICAgICAgICAgICAgICApCnAyPC1nZ3Bsb3QoZGF0YT1hLCBhZXMoeD1OTURTMSwgeT1OTURTMywgY29sb3IgPSBnZW5kZXIpICkrCiAgZ2VvbV9wb2ludCgpKwogIHN0YXRfZWxsaXBzZSgpKwogIGxhYnMoeT0iTURTMyIsIHg9Ik1EUzEiKSsKICBzY2FsZV9zaGFwZV9tYW51YWwodmFsdWVzPWMoMTYsMTcpLAogICAgICAgICAgICAgICAgICAgICBndWlkZSA9CiAgICAgICAgICAgICAgICAgICAgICAgICBndWlkZV9sZWdlbmQobGFiZWwudGhlbWUgPSBlbGVtZW50X3RleHQoc2l6ZT02KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGl0bGU9TlVMTCkKICAgICAgICAgICAgICAgICAgICAgKQoKcDEgKyBwMiArIHBsb3RfbGF5b3V0KGd1aWRlcz0nY29sbGVjdCcpICsgcGxvdF9hbm5vdGF0aW9uKHRpdGxlPSJTZXgiKSAmIHRoZW1lX3FrKCkgJiBzY2FsZV9jb2xvcl91Y2hpY2FnbygpCgpwMTwtZ2dwbG90KGRhdGE9YSwgYWVzKHg9Tk1EUzEsIHk9Tk1EUzIsIGNvbG9yPXNtb2tpbmdfY3VycmVudCkgKSsKICBnZW9tX3BvaW50KCkrCiAgc3RhdF9lbGxpcHNlKCkrCiAgbGFicyh5PSJNRFMyIiwgeD0iTURTMSIpKwogIHNjYWxlX3NoYXBlX21hbnVhbCh2YWx1ZXM9YygxNiwxNyksCiAgICAgICAgICAgICAgICAgICAgIGd1aWRlID0KICAgICAgICAgICAgICAgICAgICAgICAgIGd1aWRlX2xlZ2VuZChsYWJlbC50aGVtZSA9IGVsZW1lbnRfdGV4dChzaXplPTYpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aXRsZT1OVUxMKQogICAgICAgICAgICAgICAgICAgICApCnAyPC1nZ3Bsb3QoZGF0YT1hLCBhZXMoeD1OTURTMSwgeT1OTURTMywgY29sb3IgPSBzbW9raW5nX2N1cnJlbnQpICkrCiAgZ2VvbV9wb2ludCgpKwogIHN0YXRfZWxsaXBzZSgpKwogIGxhYnMoeT0iTURTMyIsIHg9Ik1EUzEiKSsKICBzY2FsZV9zaGFwZV9tYW51YWwodmFsdWVzPWMoMTYsMTcpLAogICAgICAgICAgICAgICAgICAgICBndWlkZSA9CiAgICAgICAgICAgICAgICAgICAgICAgICBndWlkZV9sZWdlbmQobGFiZWwudGhlbWUgPSBlbGVtZW50X3RleHQoc2l6ZT02KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGl0bGU9TlVMTCkKICAgICAgICAgICAgICAgICAgICAgKQoKcDEgKyBwMiArIHBsb3RfbGF5b3V0KGd1aWRlcz0nY29sbGVjdCcpICsgcGxvdF9hbm5vdGF0aW9uKHRpdGxlPSJDdXJyZW50IHNtb2tpbmciKSAmIHRoZW1lX3FrKCkgJiBzY2FsZV9jb2xvcl91Y2hpY2FnbygpCgpgYGAKCj5Ucnkgc29tZSB2aXN1YWxpemF0aW9ucyBvZiBwYWlycyBvZiBmYWN0b3JzIHVzaW5nIE1EUzEgJiBNRFMyICh3aGlsZSAzRCBpcyBwcmVmZXJyZWQsIHN0cmVzcyBvZiAzRCBpcyBhcm91bmQgLjE1LCB2cyAwLjIyLCBzbyBub3QgaHVnZSBkaWZmZXJlbmNlKQo+U2VwYXJhdGUgSElWKy8tLCB0aGVuIHVzZSBzeW1ib2wgY29sb3VyIHRvIHNlcGFyYXRlIHNlY29uZCBmYWN0b3IuIAoKYGBge3J9CnAxPC1nZ3Bsb3QoZGF0YT1zdWJzZXQoYSwgY2FuZGlkYT09IlllcyIpLCBhZXMoeD1OTURTMSwgeT1OTURTMiwgY29sb3I9SElWKSApKwogIGdlb21fcG9pbnQoKSsKICBzdGF0X2VsbGlwc2UoKSsKICBsYWJzKHk9Ik1EUzIiLCB4PSJNRFMxIiwKICAgICAgIHRpdGxlPSJDYW5kaWRhIikrCiAgc2NhbGVfc2hhcGVfbWFudWFsKAogICAgICAgICAgICAgICAgICAgICBndWlkZSA9CiAgICAgICAgICAgICAgICAgICAgICAgICBndWlkZV9sZWdlbmQobGFiZWwudGhlbWUgPSBlbGVtZW50X3RleHQoc2l6ZT02KSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGl0bGU9TlVMTCkKICAgICAgICAgICAgICAgICAgICAgKQpwMjwtZ2dwbG90KGRhdGE9c3Vic2V0KGEsIGNhbmRpZGEgPT0iTm8iKSwgYWVzKHg9Tk1EUzEsIHk9Tk1EUzIsIGNvbG9yID0gSElWKSApKwogIGdlb21fcG9pbnQoKSsKICBzdGF0X2VsbGlwc2UoKSsKICBsYWJzKHk9Ik1EUzIiLCB4PSJNRFMxIiwKICAgICAgIHRpdGxlID0gIk5vIENhbmRpZGEiKSsKICBzY2FsZV9zaGFwZV9tYW51YWwoCiAgICAgICAgICAgICAgICAgICAgIGd1aWRlID0KICAgICAgICAgICAgICAgICAgICAgICAgIGd1aWRlX2xlZ2VuZChsYWJlbC50aGVtZSA9IGVsZW1lbnRfdGV4dChzaXplPTYpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aXRsZT1OVUxMKQogICAgICAgICAgICAgICAgICAgICApCnBfY29tYmluZWQgPC0gcDEgKyBwMiArIHBsb3RfbGF5b3V0KGd1aWRlcz0nY29sbGVjdCcpICYgdGhlbWVfcWsoKSAmIHNjYWxlX2NvbG9yX3VjaGljYWdvKCkKcF9yYW5nZXNfeCA8LSBjKGdncGxvdF9idWlsZChwX2NvbWJpbmVkW1sxXV0pJGxheW91dCRwYW5lbF9zY2FsZXNfeFtbMV1dJHJhbmdlJHJhbmdlLAogIGdncGxvdF9idWlsZChwX2NvbWJpbmVkW1syXV0pJGxheW91dCRwYW5lbF9zY2FsZXNfeFtbMV1dJHJhbmdlJHJhbmdlKQoKcF9yYW5nZXNfeSA8LSBjKGdncGxvdF9idWlsZChwX2NvbWJpbmVkW1sxXV0pJGxheW91dCRwYW5lbF9zY2FsZXNfeVtbMV1dJHJhbmdlJHJhbmdlLAogICAgICAgICAgICAgICAgZ2dwbG90X2J1aWxkKHBfY29tYmluZWRbWzJdXSkkbGF5b3V0JHBhbmVsX3NjYWxlc195W1sxXV0kcmFuZ2UkcmFuZ2UpCnBfY29tYmluZWQgJiAKICB4bGltKG1pbihwX3Jhbmdlc194KSwgbWF4KHBfcmFuZ2VzX3gpKSAmIAogIHlsaW0obWluKHBfcmFuZ2VzX3kpLCBtYXgocF9yYW5nZXNfeSkpCmBgYAoKYGBge3J9CnAxPC1nZ3Bsb3QoZGF0YT1zdWJzZXQoYSwgSElWPT0iWWVzIiksIGFlcyh4PU5NRFMxLCB5PU5NRFMyLCBjb2xvcj1jYW5kaWRhKSApKwogIGdlb21fcG9pbnQoKSsKICBzdGF0X2VsbGlwc2UoKSsKICBsYWJzKHk9Ik1EUzIiLCB4PSJNRFMxIiwKICAgICAgIHRpdGxlPSJISVYrIikrCiAgc2NhbGVfc2hhcGVfbWFudWFsKAogICAgICAgICAgICAgICAgICAgICBndWlkZSA9CiAgICAgICAgICAgICAgICAgICAgICAgICBndWlkZV9sZWdlbmQobGFiZWwudGhlbWUgPSBlbGVtZW50X3RleHQoc2l6ZT02KSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGl0bGU9TlVMTCkKICAgICAgICAgICAgICAgICAgICAgKQpwMjwtZ2dwbG90KGRhdGE9c3Vic2V0KGEsIEhJViA9PSJObyIpLCBhZXMoeD1OTURTMSwgeT1OTURTMiwgY29sb3IgPSBjYW5kaWRhKSApKwogIGdlb21fcG9pbnQoKSsKICBzdGF0X2VsbGlwc2UoKSsKICBsYWJzKHk9Ik1EUzIiLCB4PSJNRFMxIiwKICAgICAgIHRpdGxlID0gIkhJVi0iKSsKICBzY2FsZV9zaGFwZV9tYW51YWwoCiAgICAgICAgICAgICAgICAgICAgIGd1aWRlID0KICAgICAgICAgICAgICAgICAgICAgICAgIGd1aWRlX2xlZ2VuZChsYWJlbC50aGVtZSA9IGVsZW1lbnRfdGV4dChzaXplPTYpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aXRsZT1OVUxMKQogICAgICAgICAgICAgICAgICAgICApCnBfY29tYmluZWQgPC0gcDEgKyBwMiArIHBsb3RfbGF5b3V0KGd1aWRlcz0nY29sbGVjdCcpICYgdGhlbWVfcWsoKSAmIHNjYWxlX2NvbG9yX3VjaGljYWdvKCkKcF9yYW5nZXNfeCA8LSBjKGdncGxvdF9idWlsZChwX2NvbWJpbmVkW1sxXV0pJGxheW91dCRwYW5lbF9zY2FsZXNfeFtbMV1dJHJhbmdlJHJhbmdlLAogIGdncGxvdF9idWlsZChwX2NvbWJpbmVkW1syXV0pJGxheW91dCRwYW5lbF9zY2FsZXNfeFtbMV1dJHJhbmdlJHJhbmdlKQoKcF9yYW5nZXNfeSA8LSBjKGdncGxvdF9idWlsZChwX2NvbWJpbmVkW1sxXV0pJGxheW91dCRwYW5lbF9zY2FsZXNfeVtbMV1dJHJhbmdlJHJhbmdlLAogICAgICAgICAgICAgICAgZ2dwbG90X2J1aWxkKHBfY29tYmluZWRbWzJdXSkkbGF5b3V0JHBhbmVsX3NjYWxlc195W1sxXV0kcmFuZ2UkcmFuZ2UpCnBfY29tYmluZWQgJiAKICB4bGltKG1pbihwX3Jhbmdlc194KSwgbWF4KHBfcmFuZ2VzX3gpKSAmIAogIHlsaW0obWluKHBfcmFuZ2VzX3kpLCBtYXgocF9yYW5nZXNfeSkpCmBgYAoKYGBge3J9CnAxPC1nZ3Bsb3QoZGF0YT1zdWJzZXQoYSwgSElWPT0iWWVzIiksIGFlcyh4PU5NRFMxLCB5PU5NRFMyLCBjb2xvciA9IHNtb2tpbmdfY3VycmVudCkgKSsKICBnZW9tX3BvaW50KCkrCiAgc3RhdF9lbGxpcHNlKCkrCiAgbGFicyh5PSJNRFMyIiwgeD0iTURTMSIsCiAgICAgICB0aXRsZT0iSElWKyIpKwogIHNjYWxlX3NoYXBlX21hbnVhbCgKICAgICAgICAgICAgICAgICAgICAgZ3VpZGUgPQogICAgICAgICAgICAgICAgICAgICAgICAgZ3VpZGVfbGVnZW5kKGxhYmVsLnRoZW1lID0gZWxlbWVudF90ZXh0KHNpemU9NiksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRpdGxlPU5VTEwpCiAgICAgICAgICAgICAgICAgICAgICkKcDI8LWdncGxvdChkYXRhPXN1YnNldChhLCBISVYgPT0iTm8iKSwgYWVzKHg9Tk1EUzEsIHk9Tk1EUzIsIGNvbG9yID0gc21va2luZ19jdXJyZW50KSApKwogIGdlb21fcG9pbnQoKSsKICBzdGF0X2VsbGlwc2UoKSsKICBsYWJzKHk9Ik1EUzIiLCB4PSJNRFMxIiwKICAgICAgIHRpdGxlID0gIkhJVi0iKSsKICBzY2FsZV9zaGFwZV9tYW51YWwoCiAgICAgICAgICAgICAgICAgICAgIGd1aWRlID0KICAgICAgICAgICAgICAgICAgICAgICAgIGd1aWRlX2xlZ2VuZChsYWJlbC50aGVtZSA9IGVsZW1lbnRfdGV4dChzaXplPTYpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aXRsZT1OVUxMKQogICAgICAgICAgICAgICAgICAgICApCnBfY29tYmluZWQgPC0gcDEgKyBwMiArIHBsb3RfbGF5b3V0KGd1aWRlcz0nY29sbGVjdCcpICYgdGhlbWVfcWsoKSAmIHNjYWxlX2NvbG9yX3VjaGljYWdvKCkKcF9yYW5nZXNfeCA8LSBjKGdncGxvdF9idWlsZChwX2NvbWJpbmVkW1sxXV0pJGxheW91dCRwYW5lbF9zY2FsZXNfeFtbMV1dJHJhbmdlJHJhbmdlLAogIGdncGxvdF9idWlsZChwX2NvbWJpbmVkW1syXV0pJGxheW91dCRwYW5lbF9zY2FsZXNfeFtbMV1dJHJhbmdlJHJhbmdlKQoKcF9yYW5nZXNfeSA8LSBjKGdncGxvdF9idWlsZChwX2NvbWJpbmVkW1sxXV0pJGxheW91dCRwYW5lbF9zY2FsZXNfeVtbMV1dJHJhbmdlJHJhbmdlLAogICAgICAgICAgICAgICAgZ2dwbG90X2J1aWxkKHBfY29tYmluZWRbWzJdXSkkbGF5b3V0JHBhbmVsX3NjYWxlc195W1sxXV0kcmFuZ2UkcmFuZ2UpCnBfY29tYmluZWQgJiAKICB4bGltKG1pbihwX3Jhbmdlc194KSwgbWF4KHBfcmFuZ2VzX3gpKSAmIAogIHlsaW0obWluKHBfcmFuZ2VzX3kpLCBtYXgocF9yYW5nZXNfeSkpCmBgYAoKCiMjIFJlZmVyZW5jZXMK